<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://docs.moodle.org/dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Brianking</id>
	<title>MoodleDocs - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://docs.moodle.org/dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Brianking"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/Special:Contributions/Brianking"/>
	<updated>2026-08-14T22:20:56Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Local_plugins&amp;diff=47297</id>
		<title>Local plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Local_plugins&amp;diff=47297"/>
		<updated>2015-01-30T14:11:48Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Examples */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Local customisations&lt;br /&gt;
|state = Implemented&lt;br /&gt;
|tracker = MDL-17376, MDL-16438&lt;br /&gt;
|discussion = http://moodle.org/mod/forum/discuss.php?d=126017 http://moodle.org/mod/forum/discuss.php?d=86903&lt;br /&gt;
|assignee = [[User:Petr Škoda (škoďák)|Petr Škoda (škoďák)]], some parts were originally proposed and implemented in 1.9 by Penny Leach&lt;br /&gt;
}}&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The recommended way to add new functionality to Moodle is to create a new standard plugin (module, block, auth, enrol, etc.).The /local/ plugins are mostly suitable for things that do not fit standard plugins.&lt;br /&gt;
&lt;br /&gt;
== Custom /local/ plugins ==&lt;br /&gt;
&lt;br /&gt;
Local plugins are used in cases when no standard plugin fits, examples are:&lt;br /&gt;
* event consumers communicating with external systems&lt;br /&gt;
* custom definitions of web services and external functions&lt;br /&gt;
* applications that extend moodle at the system level (hub server, amos server, etc.)&lt;br /&gt;
* new database tables used in core hacks (discouraged)&lt;br /&gt;
* new capability definitions used in core hacks&lt;br /&gt;
* custom admin settings&lt;br /&gt;
* extending the navigation block with custom menus [http://moodle.org/mod/forum/discuss.php?d=170325&amp;amp;parent=753095]&lt;br /&gt;
&lt;br /&gt;
=== Standard plugin features: ===&lt;br /&gt;
* /local/xxx/[[version.php]] - version of script (must be incremented after changes)&lt;br /&gt;
* /local/xxx/db/install.xml - executed during install (new version.php found)&lt;br /&gt;
* /local/xxx/db/install.php - executed right after install.xml&lt;br /&gt;
* /local/xxx/db/uninstall.php - executed during uninstallation&lt;br /&gt;
* /local/xxx/db/upgrade.php - executed after version.php change&lt;br /&gt;
* /local/xxx/db/access.php - definition of capabilities&lt;br /&gt;
* /local/xxx/db/events.php - event handlers and subscripts&lt;br /&gt;
* /local/xxx/db/messages.php - messaging registration&lt;br /&gt;
* /local/xxx/db/external.php - web services and external functions descriptions&lt;br /&gt;
* /local/xxx/cron.php - cron job, run at the interval defined in version.php. Alternatively, you can define &amp;lt;tt&amp;gt;local_xxx_cron()&amp;lt;/tt&amp;gt; in lib.php. (The lib.php method is now preferred.)&lt;br /&gt;
* /local/xxx/lang/en/local_pluginname.php - language file&lt;br /&gt;
* /local/xxx/lib.php - function library, automatically included with by config.php.  Hook functions local_pluginname_extends_navigation() and local_pluginname_extends_settings_navigation() can be used to add items to the navigation and settings blocks&lt;br /&gt;
* /local/xxx/settings.php - configuration options. These get added to the admin menu.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;xxx&#039;&#039; is used instead of your local plugin name, plugins of the same type are installed/upgraded in alphabetical order.&lt;br /&gt;
&lt;br /&gt;
=== List of differences from normal plugins: ===&lt;br /&gt;
* always executed last during install/upgrade - guaranteed by order of plugins in &amp;lt;code&amp;gt;get_plugin_types()&amp;lt;/code&amp;gt;&lt;br /&gt;
* are expected to use event handlers - events are intended for communication core--&amp;gt;plugins only, local plugins are the best candidates for event handlers&lt;br /&gt;
* can add admin settings to any settings page - loaded last when constructing admin tree&lt;br /&gt;
* do not need to have any UI - other plugins are usually visible somewhere&lt;br /&gt;
* some extra hooks (not implemented yet)&lt;br /&gt;
&lt;br /&gt;
== /local/xxx/db/messages.php ==&lt;br /&gt;
Example File Structure:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Defines message providers (types of messages being sent)&lt;br /&gt;
 *&lt;br /&gt;
 * @package mod-forum&lt;br /&gt;
 * @copyright  1999 onwards  Martin Dougiamas  http://moodle.com&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;
$messageproviders = array (&lt;br /&gt;
&lt;br /&gt;
/// Ordinary single forum posts&lt;br /&gt;
    &#039;posts&#039; =&amp;gt; array (&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other /local/ customisation files== &lt;br /&gt;
&lt;br /&gt;
===Customised site defaults=== &lt;br /&gt;
&lt;br /&gt;
Different default site settings can be stored in file /local/defaults.php.&lt;br /&gt;
These new defaults are used during installation, upgrade and later are&lt;br /&gt;
displayed as default values in admin settings. This means that the content&lt;br /&gt;
of the defaults files is usually updated BEFORE installation or upgrade.&lt;br /&gt;
&lt;br /&gt;
These customised defaults are useful especially when using CLI tools&lt;br /&gt;
for installation and upgrade.&lt;br /&gt;
&lt;br /&gt;
Sample /local/defaults.php file content:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;forcelogin&#039;] = 1;  // new default for $CFG-&amp;gt;forcelogin&lt;br /&gt;
$defaults[&#039;scorm&#039;][&#039;maxgrade&#039;] = 20;    // default for get_config(&#039;scorm&#039;, &#039;maxgrade&#039;)&lt;br /&gt;
$defaults[&#039;moodlecourse&#039;][&#039;numsections&#039;] = 11;&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;hiddenuserfields&#039;] = array(&#039;city&#039;, &#039;country&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
First bracket contains string from column plugin of config_plugins table.&lt;br /&gt;
Second bracket is the name of setting. In the admin settings UI the plugin and&lt;br /&gt;
name of setting is separated by &amp;quot;|&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The values usually correspond to the raw string in config table, with the exception&lt;br /&gt;
of comma separated lists that are usually entered as real arrays.&lt;br /&gt;
&lt;br /&gt;
Please note that not all settings are converted to admin_tree,&lt;br /&gt;
they are mostly intended to be set directly in config.php.&lt;br /&gt;
&lt;br /&gt;
=== 2.0 pre-upgrade script===&lt;br /&gt;
&lt;br /&gt;
You can use /local/upgrade_pre20.php script for any code that needs to&lt;br /&gt;
be executed before the main upgrade to 2.0. Most probably this will&lt;br /&gt;
be used for undoing of old hacks that would otherwise break normal&lt;br /&gt;
2.0 upgrade.&lt;br /&gt;
&lt;br /&gt;
This file is just included directly, there does not need to be any&lt;br /&gt;
function inside. If the execution stops the script is executed again&lt;br /&gt;
during the next upgrade. The first execution of lib/db/upgrade.php&lt;br /&gt;
increments the version number and the pre upgrade script is not&lt;br /&gt;
executed any more.&lt;br /&gt;
&lt;br /&gt;
== Customisations outside of /local/ directory== &lt;br /&gt;
&lt;br /&gt;
=== Forced settings=== &lt;br /&gt;
&lt;br /&gt;
Sometimes it is useful to force some settings and prevent any changes of these settings via the standard admin UI. It is possible to hardcode these settings in config.php.&lt;br /&gt;
&lt;br /&gt;
In case of course settings it is very simply, the values are assigned directly to $CFG properties. In case of plugins the values are specified in a multidimensional array in $CFG-&amp;gt;force_plugin_settings.&lt;br /&gt;
&lt;br /&gt;
Sample code in config.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;allowobjectembed = 0;&lt;br /&gt;
$CFG-&amp;gt;forced_plugin_settings = array(&#039;page&#039;=&amp;gt;array(&#039;displayoptions&#039;=&amp;gt;5, &#039;requiremodintro&#039;=&amp;gt;1), &#039;folder&#039;=&amp;gt;array(&#039;requiremodintro&#039;=&amp;gt;1));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Local language customisations=== &lt;br /&gt;
&lt;br /&gt;
Moodle supports other type of local customisation of standard language&lt;br /&gt;
packs. If you want to create your own language pack based on another&lt;br /&gt;
language create new dataroot directory with &amp;quot;_local&amp;quot; suffix, for example&lt;br /&gt;
following file with content changes string &amp;quot;Login&amp;quot; to &amp;quot;Sign in&amp;quot;:&lt;br /&gt;
moodledata/lang/en_local&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
  $string[&#039;login&#039;] = &#039;Sign in&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
See also https://docs.moodle.org/en/Language_editing&lt;br /&gt;
&lt;br /&gt;
=== Custom script injection=== &lt;br /&gt;
&lt;br /&gt;
Very old customisation option that allows you to modify scripts by injecting&lt;br /&gt;
code right after the require &#039;config.php&#039; call.&lt;br /&gt;
&lt;br /&gt;
This setting is enabled by manually setting $CFG-&amp;gt;customscripts variable&lt;br /&gt;
in config.php script. The value is expected to be full path to directory&lt;br /&gt;
with the same structure as dirroot. Please note this hack only affects&lt;br /&gt;
files that actually include the config.php!&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
* disable one specific moodle page without code modification&lt;br /&gt;
* alter page parameters on the fly&lt;br /&gt;
&lt;br /&gt;
=== Direct code modifications=== &lt;br /&gt;
This is usually the last resort, if possible do not do it. And if you still do it use some version control system (preferably git).&lt;br /&gt;
&lt;br /&gt;
=== Direct database modifications=== &lt;br /&gt;
Very strongly discouraged! Sometimes field lengths may be modified without side effects. Adding or removing of db fields will most probably cause major problems during future upgrades. New database tables should be added only from plugins.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Adding an element to the settings menu ===&lt;br /&gt;
&lt;br /&gt;
In local/*pluginname*/lib.php, define a function named local_*pluginname*_extends_settings_navigation, this will get called when Moodle builds the settings block.&lt;br /&gt;
This example adds a link to the bottom of the course administration section of the settings block.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function local_myplugin_extends_settings_navigation($settingsnav, $context) {&lt;br /&gt;
    global $CFG, $PAGE;&lt;br /&gt;
&lt;br /&gt;
    // Only add this settings item on non-site course pages.&lt;br /&gt;
    if (!$PAGE-&amp;gt;course or $PAGE-&amp;gt;course-&amp;gt;id == 1) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Only let users with the appropriate capability see this settings item.&lt;br /&gt;
    if (!has_capability(&#039;moodle/backup:backupcourse&#039;, context_course::instance($PAGE-&amp;gt;course-&amp;gt;id))) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($settingnode = $settingsnav-&amp;gt;find(&#039;courseadmin&#039;, navigation_node::TYPE_COURSE)) {&lt;br /&gt;
        $strfoo = get_string(&#039;foo&#039;, &#039;local_myplugin&#039;);&lt;br /&gt;
        $url = new moodle_url(&#039;/local/myplugin/foo.php&#039;, array(&#039;id&#039; =&amp;gt; $PAGE-&amp;gt;course-&amp;gt;id));&lt;br /&gt;
        $foonode = navigation_node::create(&lt;br /&gt;
            $strfoo,&lt;br /&gt;
            $url,&lt;br /&gt;
            navigation_node::NODETYPE_LEAF,&lt;br /&gt;
            &#039;myplugin&#039;,&lt;br /&gt;
            &#039;myplugin&#039;,&lt;br /&gt;
            new pix_icon(&#039;t/addcontact&#039;, $strfoo)&lt;br /&gt;
        );&lt;br /&gt;
        if ($PAGE-&amp;gt;url-&amp;gt;compare($url, URL_MATCH_BASE)) {&lt;br /&gt;
            $foonode-&amp;gt;make_active();&lt;br /&gt;
        }&lt;br /&gt;
        $settingnode-&amp;gt;add_node($foonode);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Removing the &amp;quot;Site Home&amp;quot; link from the navigation menu ===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function local_xxx_extends_navigation(global_navigation $navigation) {&lt;br /&gt;
    if ($home = $navigation-&amp;gt;find(&#039;home&#039;, global_navigation::TYPE_SETTING)) {&lt;br /&gt;
        $home-&amp;gt;remove();&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local customisations in previous versions ==&lt;br /&gt;
Previous versions include only partial support for customisations in /local/ directory.&lt;br /&gt;
&lt;br /&gt;
=== List of local customisations in 1.9.x: ===&lt;br /&gt;
* /local/cron.php - custom cron jobs&lt;br /&gt;
* /local/settings.php - custom admin settings&lt;br /&gt;
* /local/db/upgrade.php - general modifications&lt;br /&gt;
* /local/lang/* - custom strings&lt;br /&gt;
* /local/lib.php - local_delete_course()&lt;br /&gt;
&lt;br /&gt;
=== Migration from old 1.9.x /local/: ===&lt;br /&gt;
* &amp;lt;code&amp;gt;local/*&amp;lt;/code&amp;gt; needs to be copied to new directory&lt;br /&gt;
* &amp;lt;code&amp;gt;local/xxxx/db/install.php&amp;lt;/code&amp;gt; is intended for first installation, originally everything was in upgrade.php&lt;br /&gt;
* events are used instead of hooks&lt;br /&gt;
* upgrade code needs to migrate old settings, events, etc. directly in core db tables - such as change component strings and capability names from db/install.php or manually before/after upgrade&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170325&amp;amp;parent=753095 Extending navigation block]&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=86903 Local Customisations] forum discussion&lt;br /&gt;
* http://cvs.moodle.org/moodle/local/readme.txt?view=markup&lt;br /&gt;
* [[Local customisation (Moodle 1.9)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Task_API&amp;diff=45317</id>
		<title>Task API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Task_API&amp;diff=45317"/>
		<updated>2014-06-16T15:34:16Z</updated>

		<summary type="html">&lt;p&gt;Brianking: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Task&lt;br /&gt;
|state = Integrated&lt;br /&gt;
|tracker = MDL-25505&lt;br /&gt;
|discussion = https://moodle.org/mod/forum/discuss.php?d=229139&lt;br /&gt;
|assignee = Damyon&lt;br /&gt;
}}&lt;br /&gt;
{{Moodle 2.7}}&lt;br /&gt;
== Tasks ==&lt;br /&gt;
&lt;br /&gt;
A task is a unit of work that needs to be done later. Good uses for tasks:&lt;br /&gt;
&lt;br /&gt;
* Run a slow operation in the background&lt;br /&gt;
* Run a maintenance task on a regular schedule&lt;br /&gt;
&lt;br /&gt;
In general any operation that takes more than a few seconds should be a candidate for a task.&lt;br /&gt;
&lt;br /&gt;
== Benefits ==&lt;br /&gt;
&lt;br /&gt;
* Better user experience (give them feedback immediately, that their task has been queued)&lt;br /&gt;
* Prevent browser timeouts&lt;br /&gt;
* Better performance for clusters (tasks can be run on separate, non-webserving cluster node, tasks can run in parallel)&lt;br /&gt;
* Failed tasks will be retried&lt;br /&gt;
* A better user interface will prevent users queuing multiple tasks, because they thought it had &amp;quot;got stuck&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Types of task ==&lt;br /&gt;
&lt;br /&gt;
=== Scheduled tasks ===&lt;br /&gt;
Scheduled tasks are tasks that will run on a regular schedule. A default schedule can be set, but admins have the ability to change the default schedule if required. Note: Tasks will only run as often as cron is run in Moodle. In 2.7 it is recommended to run cron once per minute to get the benefit from the new task scheduling (don&#039;t worry - it will do much less work each time it runs).&lt;br /&gt;
&lt;br /&gt;
=== Adhoc tasks ===&lt;br /&gt;
Adhoc tasks are for when you need to queue something to run in the background immediately. Adhoc tasks can contain custom data, specific to this specific instance of the task.&lt;br /&gt;
&lt;br /&gt;
== Usage ==&lt;br /&gt;
&lt;br /&gt;
=== Scheduled task usage ===&lt;br /&gt;
&lt;br /&gt;
Scheduled tasks are created by subclassing \core\task\scheduled_task. They also require an entry in &amp;quot;db/tasks.php&amp;quot; for your plugin.&lt;br /&gt;
&lt;br /&gt;
1. Create a subclass of \core\task\scheduled_task that contains your code to run in a schedule (within &amp;quot;&amp;lt;pluginroot&amp;gt;/classes/task/cut_my_toe_nails.php&amp;quot; for the autoloading mechanism to pick up).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
namespace mod_hygene\task;&lt;br /&gt;
&lt;br /&gt;
class cut_my_toe_nails extends \core\task\scheduled_task {      &lt;br /&gt;
    public function get_name() {&lt;br /&gt;
        // Shown in admin screens&lt;br /&gt;
        return get_string(&#039;cutmytoenails&#039;, &#039;mod_hygene&#039;);&lt;br /&gt;
    }&lt;br /&gt;
                                                                     &lt;br /&gt;
    public function execute() {       &lt;br /&gt;
        // apply fungus cream&lt;br /&gt;
        // apply chainsaw&lt;br /&gt;
        // apply olive oil&lt;br /&gt;
    }                                                                                                                               &lt;br /&gt;
} &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
2. Create entry in db/tasks.php for your plugin (then a version bump to your plugin will install the task):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$tasks = array(                                                                                                                     &lt;br /&gt;
    array(                                                                                                                          &lt;br /&gt;
        &#039;classname&#039; =&amp;gt; &#039;mod_hygene\task\cut_my_toe_nails&#039;,                                                                            &lt;br /&gt;
        &#039;blocking&#039; =&amp;gt; 0,                                                                                                            &lt;br /&gt;
        &#039;minute&#039; =&amp;gt; &#039;*&#039;,                                                                                                            &lt;br /&gt;
        &#039;hour&#039; =&amp;gt; &#039;*&#039;,                                                                                                              &lt;br /&gt;
        &#039;day&#039; =&amp;gt; &#039;*&#039;,                                                                                                               &lt;br /&gt;
        &#039;dayofweek&#039; =&amp;gt; &#039;*&#039;,                                                                                                         &lt;br /&gt;
        &#039;month&#039; =&amp;gt; &#039;1,6&#039;                                                                                                              &lt;br /&gt;
    )&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
The field required in the db/tasks.php file are:&lt;br /&gt;
* classname - the fully namespaced classname of your task. This needs to comply with the autoloading rules. &lt;br /&gt;
* blocking - if this is set to 1, no other scheduled task will run at the same time as this task. Do not set this to 1 unless you really need it as it will impact the performance of the task queue.&lt;br /&gt;
* minute, hour, day, dayofweek, month - This is the default schedule for running the task. The syntax matches the syntax of unix cron. &lt;br /&gt;
==== Cron syntax examples ====&lt;br /&gt;
* day - Day of month field for task schedule. &lt;br /&gt;
** &amp;quot;*&amp;quot; - Every day&lt;br /&gt;
** &amp;quot;*/2&amp;quot; - Every 2nd day&lt;br /&gt;
** &amp;quot;1&amp;quot; - The first of every month&lt;br /&gt;
** &amp;quot;1,15&amp;quot; - The first and fifteenth of every month&lt;br /&gt;
* dayofweek - Day of week field for task schedule.&lt;br /&gt;
** &amp;quot;*&amp;quot; - Every day&lt;br /&gt;
** &amp;quot;0&amp;quot; - Every Sunday&lt;br /&gt;
** &amp;quot;6&amp;quot; - Every Saturday&lt;br /&gt;
** &amp;quot;1,5&amp;quot; - Every Monday and Friday&lt;br /&gt;
* hour - Hour field for task schedule.&lt;br /&gt;
** &amp;quot;*&amp;quot; - Every hour&lt;br /&gt;
** &amp;quot;*/2&amp;quot; - Every 2 hours&lt;br /&gt;
** &amp;quot;2-10&amp;quot; - Every hour from 2am until 10am (inclusive)&lt;br /&gt;
** &amp;quot;2,6,9&amp;quot; - 2am, 6am and 9am&lt;br /&gt;
* minute - Minute field for task schedule.&lt;br /&gt;
** &amp;quot;*&amp;quot; - Every minute&lt;br /&gt;
** &amp;quot;*/5&amp;quot; - Every 5 minutes&lt;br /&gt;
** &amp;quot;2-10&amp;quot; - Every minute between 2 and 10 minutes past the hour (inclusive)&lt;br /&gt;
** &amp;quot;2,6,9&amp;quot; - 2, 6 and 9 minutes past the hour&lt;br /&gt;
* month - Month field for task schedule.&lt;br /&gt;
** &amp;quot;*&amp;quot; - Every month&lt;br /&gt;
** &amp;quot;*/2&amp;quot; - Every second month&lt;br /&gt;
** &amp;quot;1&amp;quot; - Every January&lt;br /&gt;
** &amp;quot;1,5&amp;quot; - Every January and May&lt;br /&gt;
&lt;br /&gt;
=== Adhoc task usage ===&lt;br /&gt;
&lt;br /&gt;
This is even easier than scheduled tasks. &lt;br /&gt;
&lt;br /&gt;
1. Create a subclass of \core\task\adhoc_task that contains your code to run in the background.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
class take_over_the_world extends \core\task\adhoc_task {                                                                           &lt;br /&gt;
    public function execute() {       &lt;br /&gt;
        // gain 100,000,000 friends on facebook.&lt;br /&gt;
        // crash the stock market.&lt;br /&gt;
        // run for president.&lt;br /&gt;
    }                                                                                                                               &lt;br /&gt;
} &lt;br /&gt;
                                      &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
2. Create an instance of the task and queue it (adding custom data if required).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   // create the instance&lt;br /&gt;
   $domination = new take_over_the_world();&lt;br /&gt;
   // set blocking if required (it probably isn&#039;t)&lt;br /&gt;
   // $domination-&amp;gt;set_blocking(true);&lt;br /&gt;
   // add custom data&lt;br /&gt;
   $domination-&amp;gt;set_custom_data(array(&lt;br /&gt;
       &#039;plansfortomorrownight&#039; =&amp;gt; &#039;The same thing we do every night, Pinky!&#039;&lt;br /&gt;
   ));&lt;br /&gt;
&lt;br /&gt;
   // queue it&lt;br /&gt;
   \core\task\manager::queue_adhoc_task($domination);&lt;br /&gt;
&lt;br /&gt;
   // profit&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
3. There is no 3.&lt;br /&gt;
&lt;br /&gt;
=== Failures ===&lt;br /&gt;
A task, either scheduled or adhoc can sometimes fail. An example would be updating an RSS field when the network is temporarily down. This is handled by the task system automatically - all the failing task needs to do is throw an exception. The task will be retried after 1 minute. If the task keeps failing, the retry algorithm will add more time between each successive attempts up to a max of 24 hours.&lt;br /&gt;
&lt;br /&gt;
=== Caches ===&lt;br /&gt;
There is one special case that needs to be considered with this new system. If a particular scheduled or adhoc task runs for a long time and updates many DB records - particularly something related to enrolment - the next task in the queue may suffer because various API&#039;s in moodle use static caching to speed up requests, but assume that the data will not change much between the start and end of the request. In this case, you can force the cron to exit after running a task that has done many DB updates. The next cron will be run in the next minute, and will have all static caches cleared because it&#039;s a new process. To do this call \core\task\manager::clear_static_caches();&lt;br /&gt;
&lt;br /&gt;
=== Security ===&lt;br /&gt;
When scheduling a task to run in the background - or creating a scheduled task, the task will run in the context of the cron user (see &amp;quot;cron_setup_user()&amp;quot;). If you need to perform access checks in your background task, you should pass the userid/context in custom_data and then pass that userid to the access check functions (&amp;quot;require_capability(&#039;moodle/course:update&#039;,  $context, $userid)&amp;quot;).&lt;br /&gt;
&lt;br /&gt;
=== Legacy cron ===&lt;br /&gt;
The older syntax of cron.php or modname_cron() is still supported - but is not as good as this new API. This is because:&lt;br /&gt;
* the legacy cron functions run serially - a long running cron in one plugin will hold up the other plugins crons&lt;br /&gt;
* the legacy cron functions are fragile - a failure in one cron in one plugin will prevent the cron in other functions from running at all&lt;br /&gt;
* the scheduling cannot be changed by admins&lt;br /&gt;
&lt;br /&gt;
== For Admins ==&lt;br /&gt;
Admins have a new screen where they can adjust the schedules for any scheduled task. They can also reset any scheduled task to it&#039;s default schedule.&lt;br /&gt;
[[File:task_admin_screenshot.png|Screenshot of admin page]]&lt;br /&gt;
&lt;br /&gt;
== Specification ==&lt;br /&gt;
The specification for this feature is here: https://docs.moodle.org/dev/Scheduled_Tasks_Proposal&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=44285</id>
		<title>Old Events API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=44285"/>
		<updated>2014-04-03T12:56:50Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Events wishlist */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Overview==&lt;br /&gt;
&lt;br /&gt;
The Events API is a core system in Moodle to allow communication between modules.  &lt;br /&gt;
&lt;br /&gt;
An &#039;&#039;&#039;event&#039;&#039;&#039; is when something &amp;quot;interesting&amp;quot; happens in Moodle that is worth alerting the system about.&lt;br /&gt;
&lt;br /&gt;
Any Moodle modules can &#039;&#039;&#039;trigger&#039;&#039;&#039; new events (with attached data), and other modules can elect to &#039;&#039;&#039;handle&#039;&#039;&#039; those events with custom functions that operate on the given data.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s look at an example of how events are used to implement enrolment in Moodle 2.0. In the enrolment system, lib/enrollib.php will generate a &#039;user_enrolled&#039; event upon completion of an enrolment of a user.&lt;br /&gt;
&lt;br /&gt;
===Triggering an event===&lt;br /&gt;
&lt;br /&gt;
When a user is enrolled, a &#039;user_enrolled&#039; event is built and sent out by the enrol API. In this example let&#039;s pretend someone was just enrolled.&lt;br /&gt;
&lt;br /&gt;
The enrol lib (could even be a module thats creating this event) needs to create an object with the data that this event needs. This may vary completely for different types of events, it&#039;s just a data object.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$ue = new stdClass();&lt;br /&gt;
$ue-&amp;gt;enrolid      = $instance-&amp;gt;id;&lt;br /&gt;
$ue-&amp;gt;status       = is_null($status) ? ENROL_USER_ACTIVE : $status;&lt;br /&gt;
$ue-&amp;gt;userid       = $userid;&lt;br /&gt;
$ue-&amp;gt;timestart    = $timestart;&lt;br /&gt;
$ue-&amp;gt;timeend      = $timeend;&lt;br /&gt;
$ue-&amp;gt;modifierid   = $USER-&amp;gt;id;&lt;br /&gt;
...&lt;br /&gt;
$ue-&amp;gt;courseid  = $courseid;&lt;br /&gt;
$ue-&amp;gt;enrol     = $name;&lt;br /&gt;
&lt;br /&gt;
events_trigger(&#039;user_enrolled&#039;, $ue);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Then we post the object as an event and forget about it:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
events_trigger(&#039;user_enrolled&#039;, $eventdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
We have now broadcast an event that we think other parts of the system might need to know about.&lt;br /&gt;
&lt;br /&gt;
===Handling an event===&lt;br /&gt;
&lt;br /&gt;
Modules or core code can define an events.php in under its */db directory which defines events they want to be notified about, and describes which of their functions or class methods should be notified.  For this case, there is this definition of a handler in mod/forum/db/events.php.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$handlers = array (&lt;br /&gt;
    &#039;user_enrolled&#039; =&amp;gt; array (&lt;br /&gt;
        &#039;handlerfile&#039;      =&amp;gt; &#039;/mod/forum/lib.php&#039;,&lt;br /&gt;
        &#039;handlerfunction&#039;  =&amp;gt; &#039;forum_user_enrolled&#039;,&lt;br /&gt;
        &#039;schedule&#039;         =&amp;gt; &#039;instant&#039;,&lt;br /&gt;
        &#039;internal&#039;         =&amp;gt; 1,&lt;br /&gt;
    ),&lt;br /&gt;
&lt;br /&gt;
    &#039;user_unenrolled&#039; =&amp;gt; array (&lt;br /&gt;
        &#039;handlerfile&#039;      =&amp;gt; &#039;/mod/forum/lib.php&#039;,&lt;br /&gt;
        &#039;handlerfunction&#039;  =&amp;gt; &#039;forum_user_unenrolled&#039;,&lt;br /&gt;
        &#039;schedule&#039;         =&amp;gt; &#039;instant&#039;,&lt;br /&gt;
        &#039;internal&#039;         =&amp;gt; 1,&lt;br /&gt;
    ),&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These events.php files are parsed during install / upgrade and stored in a simple database table.&lt;br /&gt;
&lt;br /&gt;
Now, when a &#039;&#039;&#039;user_enrolled&#039;&#039;&#039; event happens, all the registered handlers functions for that event will be called something like this (but with more error handling):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
          include_once($CFG-&amp;gt;dirroot.$handlers[&#039;user_enrolled&#039;][&#039;handlerfile&#039;]);&lt;br /&gt;
          call_user_func($handlers[&#039;user_enrolled&#039;][&#039;handlerfunction&#039;], $eventdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Any code can hook into any events this way.&lt;br /&gt;
&lt;br /&gt;
The handler function accepts one parameter (the event data object) and should return a boolean.  Returning false indicates that there was an error and the event will be left in the event queue.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    function forum_user_unenrolled($eventdata) {&lt;br /&gt;
        // handle event &lt;br /&gt;
        // ...&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
note: a lib/db/events.php exists as legacy and no events should be added here. Core API is not supposed to define events for consumption. Only modules and core components (non-api) should define events for consumption/handling.&lt;br /&gt;
&lt;br /&gt;
==Database structure==&lt;br /&gt;
&lt;br /&gt;
There are 3 core tables for events. Note that if a handler is queued, and yet to be processed or processing failed, then all subsequent calls on that handler must be queued.&lt;br /&gt;
&lt;br /&gt;
===events_handlers===&lt;br /&gt;
&lt;br /&gt;
This table is for storing which components requests what type of event, and the location of the responsible handler functions.&lt;br /&gt;
&lt;br /&gt;
These entries are created by parsing events.php files in all the modules, and can be rebuilt any time (during an upgrade, say).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventname&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of the event, e.g. &#039;message_send&#039;&lt;br /&gt;
|-&lt;br /&gt;
|handlermodule&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|e.g. moodle, mod/forum, block/rss_client&lt;br /&gt;
|-&lt;br /&gt;
|handlerfile&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|path to the file of the function, eg /lib/messagelib.php&lt;br /&gt;
|-&lt;br /&gt;
|handlerfunction&lt;br /&gt;
|text&lt;br /&gt;
|serialized string or array describing function, suitable to be passed to &#039;&#039;&#039;call_user_func()&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|schedule 	&lt;br /&gt;
|varchar(255) 	&lt;br /&gt;
|&#039;cron&#039; or &#039;instant&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue===&lt;br /&gt;
&lt;br /&gt;
This table is for storing queued events. It stores only one copy of the eventdata here, and entries from this table are being references by the events_queue_handlers table.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventdata 	&lt;br /&gt;
|longtext 	&lt;br /&gt;
|serialized version of the data object passed to the event handler.&lt;br /&gt;
|-&lt;br /&gt;
|stackdump&lt;br /&gt;
|text&lt;br /&gt;
|serialized debug_backtrace showing where the event was fired from&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|int(10)&lt;br /&gt;
|$USER-&amp;gt;id when the event was fired&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) 	&lt;br /&gt;
|time stamp of the first time this was added&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue_handlers===&lt;br /&gt;
&lt;br /&gt;
This is the list of queued handlers for processing. The event object is retrieved from the events_queue table. When no further reference is made to the events_queue table, the corresponding entry in the events_queue table should be deleted. Entry should get deleted (?) after a successful event processing by the specified handler.  The status field keeps track of failures, after it gets to a certain number (eg 10?) it should trigger an &amp;quot;event failed&amp;quot; event (that could result in admin being emailed etc, or perhaps even the originating module taking care of it or rolling something back etc).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|queuedeventid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_queues table&lt;br /&gt;
|-&lt;br /&gt;
|handlerid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_handlers table&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|-&lt;br /&gt;
|errormessage&lt;br /&gt;
|text&lt;br /&gt;
|if an error happened last time we tried to process this event, record it here.&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|time stamp of the last attempt to run this from the queue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Standards for naming events==&lt;br /&gt;
&lt;br /&gt;
All event names should follow a consistent naming pattern, such as componentname_noun_verb  (See [[Frankenstyle]] about the component name)&lt;br /&gt;
&lt;br /&gt;
If the event is being fired after the action has taken place (as in most cases) then use the past tense for the verb (created / deleted / updated / sent).&lt;br /&gt;
&lt;br /&gt;
If the event &#039;&#039;&#039;is&#039;&#039;&#039; the action, then use the present tense (create / delete / update / send).&lt;br /&gt;
&lt;br /&gt;
==Events which exist==&lt;br /&gt;
&lt;br /&gt;
When we add new events to core we should always add them here too.&lt;br /&gt;
&lt;br /&gt;
Under each event, list the data sent as part of the event.&lt;br /&gt;
&lt;br /&gt;
===Users===&lt;br /&gt;
* user_created&lt;br /&gt;
** full new record from &#039;user&#039; table&lt;br /&gt;
* user_deleted&lt;br /&gt;
** record from &#039;user&#039; table before marked as deleted&lt;br /&gt;
* user_updated&lt;br /&gt;
** full new record from &#039;user&#039; table&lt;br /&gt;
* user_enrolled&lt;br /&gt;
** full user enrolment record (TBC)&lt;br /&gt;
* user_logout&lt;br /&gt;
** params TBC&lt;br /&gt;
* user_unenrol_modified&lt;br /&gt;
** full user enrolment record (TBC)&lt;br /&gt;
* user_unenrolled&lt;br /&gt;
** full user enrolment record (TBC)&lt;br /&gt;
&lt;br /&gt;
===Roles===&lt;br /&gt;
* role_assigned&lt;br /&gt;
** full new record from &#039;role_assignments&#039; table&lt;br /&gt;
* role_unassigned&lt;br /&gt;
** record from &#039;role_assignments&#039;, course context only&lt;br /&gt;
&lt;br /&gt;
===Courses===&lt;br /&gt;
* course_created&lt;br /&gt;
** full course record&lt;br /&gt;
* course_updated&lt;br /&gt;
** full course record&lt;br /&gt;
* course_deleted&lt;br /&gt;
** full course record&lt;br /&gt;
* course_category_deleted&lt;br /&gt;
** full category record&lt;br /&gt;
* course_content_removed&lt;br /&gt;
** full course record&lt;br /&gt;
* course_restored (2.5)&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
* groups_member_added&lt;br /&gt;
** groupid, userid&lt;br /&gt;
* groups_member_removed&lt;br /&gt;
** groupid, userid&lt;br /&gt;
* groups_group_created&lt;br /&gt;
** id, courseid, name, description, timecreated, timemodified, picture&lt;br /&gt;
* groups_group_updated&lt;br /&gt;
** id, courseid, name, description, timecreated, timemodified, picture&lt;br /&gt;
* groups_group_deleted&lt;br /&gt;
** id, courseid, name, description, timecreated, timemodified, picture&lt;br /&gt;
* groups_grouping_created&lt;br /&gt;
** id, courseid, name, timecreated, timemodified&lt;br /&gt;
* groups_grouping_updated&lt;br /&gt;
** id, courseid, name, timecreated, timemodified&lt;br /&gt;
* groups_grouping_deleted&lt;br /&gt;
** id, courseid, name, timecreated, timemodified&lt;br /&gt;
* groups_members_removed &#039;&#039;(user deleted from all groups in a course)&#039;&#039;&lt;br /&gt;
** courseid, userid&lt;br /&gt;
* groups_groupings_groups_removed &#039;&#039;(remove all groups from all groupings in a course)&#039;&#039;&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
* groups_groups_deleted &#039;&#039;(delete all groups in a course)&#039;&#039;&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
* groups_groupings_deleted &#039;&#039;(delete all groupings in a course)&#039;&#039;&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
&lt;br /&gt;
===Cohorts===&lt;br /&gt;
* cohort_added&lt;br /&gt;
** full cohort record (TBC)&lt;br /&gt;
* cohort_deleted&lt;br /&gt;
** full cohort record (TBC)&lt;br /&gt;
* cohort_member_added&lt;br /&gt;
** cohort ID, user ID (TBC)&lt;br /&gt;
* cohort_member_removed&lt;br /&gt;
** cohort ID, user ID (TBC)&lt;br /&gt;
* cohort_updated&lt;br /&gt;
** full cohort record (TBC)&lt;br /&gt;
&lt;br /&gt;
===Messaging===&lt;br /&gt;
* message_send&lt;br /&gt;
** component = &#039;mod/forum&#039;: path in Moodle&lt;br /&gt;
** name = &#039;posts&#039;: type of message from that module (as module defines it)&lt;br /&gt;
** userfrom = $userfrom: a user object to send from&lt;br /&gt;
** userto = $userto: a user object to send to&lt;br /&gt;
** subject = &#039;subject line&#039;: a short text line&lt;br /&gt;
** fullmessage = &#039;full plain text&#039;: raw text as entered by user&lt;br /&gt;
** fullmessageformat = FORMAT_PLAIN|FORMAT_HTML|FORMAT_MOODLE|FORMAT_MARKDOWN: the format of this text&lt;br /&gt;
** fullmessagehtml = &#039;long html text&#039;; html rendered version (optional)&lt;br /&gt;
** smallmessage = &#039;short text&#039;: useful for plugins like sms or twitter (optional)&lt;br /&gt;
&lt;br /&gt;
===Portfolio===&lt;br /&gt;
* portfolio_send&lt;br /&gt;
** id : recordid in portfolio_tempdata table, used for itemid in file storage&lt;br /&gt;
&lt;br /&gt;
===Other===&lt;br /&gt;
* assessable_file_uploaded&lt;br /&gt;
* assessable_files_done&lt;br /&gt;
* mod_created&lt;br /&gt;
* mod_deleted&lt;br /&gt;
* mod_updated&lt;br /&gt;
* quiz_attempt_started&lt;br /&gt;
* quiz_attempt_submitted - &#039;&#039;Note: these two quiz events changed in Moodle 2.1&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==Events wishlist==&lt;br /&gt;
&lt;br /&gt;
List of events which it would be nice to have.  Please add to this list if what you want is not shown here.&lt;br /&gt;
&lt;br /&gt;
* mform_print_form -- this for all types of form e.g. admin settings, user profile, module updating, + some sort of standard way of discriminating between them e.g. if ($form-&amp;gt;name == &#039;user_profile&#039;) {}. This would be better triggered at the end of the form generation process so that new bits can be inserted at any point, or existing bits could be removed.&lt;br /&gt;
* module_installed &#039;&#039;(= mod_created in v2?)&#039;&#039;&lt;br /&gt;
* module_removed &#039;&#039;(= mod_deleted in v2?)&#039;&#039;&lt;br /&gt;
* grade_update &#039;&#039;(= quiz_attempt_processed in v2?)&#039;&#039;&lt;br /&gt;
* assignment_submitted&lt;br /&gt;
* course - editing turned on or off&lt;br /&gt;
* course completed&lt;br /&gt;
* course module completion state changed (completed / not completed)&lt;br /&gt;
* course was reset&lt;br /&gt;
* Some will_be ... events that would allow another plugin to react before these things happen:&lt;br /&gt;
** course_will_be_restored (with info about course being restored and the target category)&lt;br /&gt;
** course_category_will_be_moved (with info about category being moved and target category)&lt;br /&gt;
** course_will_be_moved (with course info and target category info)&lt;br /&gt;
&lt;br /&gt;
Provide event trigger hooks for the modules, similar to what is done for the cron service which checks the LOCAL directory for a cron file. It is already possible to define an event trigger but the core/module code must be modified to actually make use of it.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Core APIs]]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=69103 Original General Developer Forum thread discussing this proposal]. &lt;br /&gt;
* [[Messaging_2.0]]&lt;br /&gt;
* [[Event_2]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Coding guidelines|Events]]&lt;br /&gt;
[[Category:Grades]]&lt;br /&gt;
[[Category:API]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=41281</id>
		<title>Old Events API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=41281"/>
		<updated>2013-07-09T07:59:02Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Events wishlist */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Overview==&lt;br /&gt;
&lt;br /&gt;
The Events API is a core system in Moodle to allow communication between modules.  &lt;br /&gt;
&lt;br /&gt;
An &#039;&#039;&#039;event&#039;&#039;&#039; is when something &amp;quot;interesting&amp;quot; happens in Moodle that is worth alerting the system about.&lt;br /&gt;
&lt;br /&gt;
Any Moodle modules can &#039;&#039;&#039;trigger&#039;&#039;&#039; new events (with attached data), and other modules can elect to &#039;&#039;&#039;handle&#039;&#039;&#039; those events with custom functions that operate on the given data.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s look at an example of how events are used to implement enrollment in Moodle 2.0.  In the enrollment system, lib/enrollib.php will generate a &#039;user_enrolled&#039; event upon completion of a enrollment of a user.&lt;br /&gt;
&lt;br /&gt;
===Triggering an event===&lt;br /&gt;
&lt;br /&gt;
When a user is enrolled, a &#039;user_enrolled&#039; event is built and sent out by the enrol API.   In this example let&#039;s pretend someone was just enrolled.&lt;br /&gt;
&lt;br /&gt;
The enrol lib (could even be a module thats creating this event) needs to create an object with the data that this event needs.  This may vary completely for different types of events, it&#039;s just a data object.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
 $ue = new stdClass();&lt;br /&gt;
 $ue-&amp;gt;enrolid      = $instance-&amp;gt;id;&lt;br /&gt;
 $ue-&amp;gt;status       = is_null($status) ? ENROL_USER_ACTIVE : $status;&lt;br /&gt;
 $ue-&amp;gt;userid       = $userid;&lt;br /&gt;
 $ue-&amp;gt;timestart    = $timestart;&lt;br /&gt;
 $ue-&amp;gt;timeend      = $timeend;&lt;br /&gt;
 $ue-&amp;gt;modifierid   = $USER-&amp;gt;id;&lt;br /&gt;
 ...&lt;br /&gt;
 $ue-&amp;gt;courseid  = $courseid;&lt;br /&gt;
 $ue-&amp;gt;enrol     = $name;&lt;br /&gt;
 &lt;br /&gt;
 events_trigger(&#039;user_enrolled&#039;, $ue);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Then we post the object as an event and forget about it.:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
 events_trigger(&#039;user_enrolled&#039;, $eventdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
We have now broadcast an event that we think other parts of the system might need to know about.&lt;br /&gt;
&lt;br /&gt;
===Handling an event===&lt;br /&gt;
&lt;br /&gt;
Modules or core code can define an events.php in under its */db directory which defines events they want to be notified about, and describes which of their functions or class methods should be notified.  For this case, there is this definition of a handler in mod/forum/db/events.php.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$handlers = array (&lt;br /&gt;
    &#039;user_enrolled&#039; =&amp;gt; array (&lt;br /&gt;
        &#039;handlerfile&#039;      =&amp;gt; &#039;/mod/forum/lib.php&#039;,&lt;br /&gt;
        &#039;handlerfunction&#039;  =&amp;gt; &#039;forum_user_enrolled&#039;,&lt;br /&gt;
        &#039;schedule&#039;         =&amp;gt; &#039;instant&#039;,&lt;br /&gt;
        &#039;internal&#039;         =&amp;gt; 1,&lt;br /&gt;
    ),&lt;br /&gt;
&lt;br /&gt;
    &#039;user_unenrolled&#039; =&amp;gt; array (&lt;br /&gt;
        &#039;handlerfile&#039;      =&amp;gt; &#039;/mod/forum/lib.php&#039;,&lt;br /&gt;
        &#039;handlerfunction&#039;  =&amp;gt; &#039;forum_user_unenrolled&#039;,&lt;br /&gt;
        &#039;schedule&#039;         =&amp;gt; &#039;instant&#039;,&lt;br /&gt;
        &#039;internal&#039;         =&amp;gt; 1,&lt;br /&gt;
    ),&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These events.php files are parsed during install / upgrade and stored in a simple database table.&lt;br /&gt;
&lt;br /&gt;
Now, when a &#039;&#039;&#039;user_enrolled&#039;&#039;&#039; event happens, all the registered handlers functions for that event will be called something like this (but with more error handling):&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
          include_once($CFG-&amp;gt;dirroot.$handlers[&#039;user_enrolled&#039;][&#039;handlerfile&#039;]);&lt;br /&gt;
          call_user_func($handlers[&#039;user_enrolled&#039;][&#039;handlerfunction&#039;], $eventdata);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Any code can hook into any events this way.&lt;br /&gt;
&lt;br /&gt;
The handler function accepts one parameter (the event data object) and should return a boolean.  Returning false indicates that there was an error and the event will be left in the event queue.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    function forum_user_unenrolled($eventdata) {&lt;br /&gt;
        // handle event &lt;br /&gt;
        // ...&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
note: a lib/db/events.php exists as legacy and no events should be added here. Core API is not supposed to define events for consumption. Only modules and core components (non-api) should define events for consumption/handling.&lt;br /&gt;
&lt;br /&gt;
==Database structure==&lt;br /&gt;
&lt;br /&gt;
There are 3 core tables for events. Note that if a handler is queued, and yet to be processed or processing failed, then all subsequent calls on that handler must be queued.&lt;br /&gt;
&lt;br /&gt;
===events_handlers===&lt;br /&gt;
&lt;br /&gt;
This table is for storing which components requests what type of event, and the location of the responsible handler functions.&lt;br /&gt;
&lt;br /&gt;
These entries are created by parsing events.php files in all the modules, and can be rebuilt any time (during an upgrade, say).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventname&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of the event, e.g. &#039;message_send&#039;&lt;br /&gt;
|-&lt;br /&gt;
|handlermodule&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|e.g. moodle, mod/forum, block/rss_client&lt;br /&gt;
|-&lt;br /&gt;
|handlerfile&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|path to the file of the function, eg /lib/messagelib.php&lt;br /&gt;
|-&lt;br /&gt;
|handlerfunction&lt;br /&gt;
|text&lt;br /&gt;
|serialized string or array describing function, suitable to be passed to &#039;&#039;&#039;call_user_func()&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|schedule 	&lt;br /&gt;
|varchar(255) 	&lt;br /&gt;
|&#039;cron&#039; or &#039;instant&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue===&lt;br /&gt;
&lt;br /&gt;
This table is for storing queued events. It stores only one copy of the eventdata here, and entries from this table are being references by the events_queue_handlers table.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventdata 	&lt;br /&gt;
|longtext 	&lt;br /&gt;
|serialized version of the data object passed to the event handler.&lt;br /&gt;
|-&lt;br /&gt;
|stackdump&lt;br /&gt;
|text&lt;br /&gt;
|serialized debug_backtrace showing where the event was fired from&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|int(10)&lt;br /&gt;
|$USER-&amp;gt;id when the event was fired&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) 	&lt;br /&gt;
|time stamp of the first time this was added&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue_handlers===&lt;br /&gt;
&lt;br /&gt;
This is the list of queued handlers for processing. The event object is retrieved from the events_queue table. When no further reference is made to the events_queue table, the corresponding entry in the events_queue table should be deleted. Entry should get deleted (?) after a successful event processing by the specified handler.  The status field keeps track of failures, after it gets to a certain number (eg 10?) it should trigger an &amp;quot;event failed&amp;quot; event (that could result in admin being emailed etc, or perhaps even the originating module taking care of it or rolling something back etc).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|queuedeventid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_queues table&lt;br /&gt;
|-&lt;br /&gt;
|handlerid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_handlers table&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|-&lt;br /&gt;
|errormessage&lt;br /&gt;
|text&lt;br /&gt;
|if an error happened last time we tried to process this event, record it here.&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|time stamp of the last attempt to run this from the queue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Standards for naming events==&lt;br /&gt;
&lt;br /&gt;
All event names should follow a consistent naming pattern, such as componentname_noun_verb  (See [[Frankenstyle]] about the component name)&lt;br /&gt;
&lt;br /&gt;
If the event is being fired after the action has taken place (as in most cases) then use the past tense for the verb (created / deleted / updated / sent).&lt;br /&gt;
&lt;br /&gt;
If the event &#039;&#039;&#039;is&#039;&#039;&#039; the action, then use the present tense (create / delete / update / send).&lt;br /&gt;
&lt;br /&gt;
==Events which exist==&lt;br /&gt;
&lt;br /&gt;
When we add new events to core we should always add them here too.&lt;br /&gt;
&lt;br /&gt;
Under each event, list the data sent as part of the event.&lt;br /&gt;
&lt;br /&gt;
===Users===&lt;br /&gt;
* user_created&lt;br /&gt;
** full new record from &#039;user&#039; table&lt;br /&gt;
* user_deleted&lt;br /&gt;
** record from &#039;user&#039; table before marked as deleted&lt;br /&gt;
* user_updated&lt;br /&gt;
** full new record from &#039;user&#039; table&lt;br /&gt;
* user_enrolled&lt;br /&gt;
** full user enrolment record (TBC)&lt;br /&gt;
* user_logout&lt;br /&gt;
** params TBC&lt;br /&gt;
* user_unenrol_modified&lt;br /&gt;
** full user enrolment record (TBC)&lt;br /&gt;
* user_unenrolled&lt;br /&gt;
** full user enrolment record (TBC)&lt;br /&gt;
&lt;br /&gt;
===Roles===&lt;br /&gt;
* role_assigned&lt;br /&gt;
** full new record from &#039;role_assignments&#039; table&lt;br /&gt;
* role_unassigned&lt;br /&gt;
** record from &#039;role_assignments&#039;, course context only&lt;br /&gt;
&lt;br /&gt;
===Courses===&lt;br /&gt;
* course_created&lt;br /&gt;
** full course record&lt;br /&gt;
* course_updated&lt;br /&gt;
** full course record&lt;br /&gt;
* course_deleted&lt;br /&gt;
** full course record&lt;br /&gt;
* course_category_deleted&lt;br /&gt;
** full category record&lt;br /&gt;
* course_content_removed&lt;br /&gt;
** full course record&lt;br /&gt;
* course_restored (2.5)&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
* groups_member_added&lt;br /&gt;
** groupid, userid&lt;br /&gt;
* groups_member_removed&lt;br /&gt;
** groupid, userid&lt;br /&gt;
* groups_group_created&lt;br /&gt;
** id, courseid, name, description, timecreated, timemodified, picture&lt;br /&gt;
* groups_group_updated&lt;br /&gt;
** id, courseid, name, description, timecreated, timemodified, picture&lt;br /&gt;
* groups_group_deleted&lt;br /&gt;
** id, courseid, name, description, timecreated, timemodified, picture&lt;br /&gt;
* groups_grouping_created&lt;br /&gt;
** id, courseid, name, timecreated, timemodified&lt;br /&gt;
* groups_grouping_updated&lt;br /&gt;
** id, courseid, name, timecreated, timemodified&lt;br /&gt;
* groups_grouping_deleted&lt;br /&gt;
** id, courseid, name, timecreated, timemodified&lt;br /&gt;
* groups_members_removed &#039;&#039;(user deleted from all groups in a course)&#039;&#039;&lt;br /&gt;
** courseid, userid&lt;br /&gt;
* groups_groupings_groups_removed &#039;&#039;(remove all groups from all groupings in a course)&#039;&#039;&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
* groups_groups_deleted &#039;&#039;(delete all groups in a course)&#039;&#039;&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
* groups_groupings_deleted &#039;&#039;(delete all groupings in a course)&#039;&#039;&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
&lt;br /&gt;
===Cohorts===&lt;br /&gt;
* cohort_added&lt;br /&gt;
** full cohort record (TBC)&lt;br /&gt;
* cohort_deleted&lt;br /&gt;
** full cohort record (TBC)&lt;br /&gt;
* cohort_member_added&lt;br /&gt;
** cohort ID, user ID (TBC)&lt;br /&gt;
* cohort_member_removed&lt;br /&gt;
** cohort ID, user ID (TBC)&lt;br /&gt;
* cohort_updated&lt;br /&gt;
** full cohort record (TBC)&lt;br /&gt;
&lt;br /&gt;
===Messaging===&lt;br /&gt;
* message_send&lt;br /&gt;
** component = &#039;mod/forum&#039;: path in Moodle&lt;br /&gt;
** name = &#039;posts&#039;: type of message from that module (as module defines it)&lt;br /&gt;
** userfrom = $userfrom: a user object to send from&lt;br /&gt;
** userto = $userto: a user object to send to&lt;br /&gt;
** subject = &#039;subject line&#039;: a short text line&lt;br /&gt;
** fullmessage = &#039;full plain text&#039;: raw text as entered by user&lt;br /&gt;
** fullmessageformat = FORMAT_PLAIN|FORMAT_HTML|FORMAT_MOODLE|FORMAT_MARKDOWN: the format of this text&lt;br /&gt;
** fullmessagehtml = &#039;long html text&#039;; html rendered version (optional)&lt;br /&gt;
** smallmessage = &#039;short text&#039;: useful for plugins like sms or twitter (optional)&lt;br /&gt;
&lt;br /&gt;
===Portfolio===&lt;br /&gt;
* portfolio_send&lt;br /&gt;
** id : recordid in portfolio_tempdata table, used for itemid in file storage&lt;br /&gt;
&lt;br /&gt;
===Other===&lt;br /&gt;
* assessable_file_uploaded&lt;br /&gt;
* assessable_files_done&lt;br /&gt;
* mod_created&lt;br /&gt;
* mod_deleted&lt;br /&gt;
* mod_updated&lt;br /&gt;
* quiz_attempt_started&lt;br /&gt;
* quiz_attempt_submitted - &#039;&#039;Note: these two quiz events changed in Moodle 2.1&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==Events wishlist==&lt;br /&gt;
&lt;br /&gt;
List of events which it would be nice to have.  Please add to this list if what you want is not shown here.&lt;br /&gt;
&lt;br /&gt;
* mform_print_form -- this for all types of form e.g. admin settings, user profile, module updating, + some sort of standard way of discriminating between them e.g. if ($form-&amp;gt;name == &#039;user_profile&#039;) {}. This would be better triggered at the end of the form generation process so that new bits can be inserted at any point, or existing bits could be removed.&lt;br /&gt;
* module_installed &#039;&#039;(= mod_created in v2?)&#039;&#039;&lt;br /&gt;
* module_removed &#039;&#039;(= mod_deleted in v2?)&#039;&#039;&lt;br /&gt;
* grade_update &#039;&#039;(= quiz_attempt_processed in v2?)&#039;&#039;&lt;br /&gt;
* assignment_submitted&lt;br /&gt;
* course completed&lt;br /&gt;
* course module completion state changed (completed / not completed)&lt;br /&gt;
* Some will_be ... events that would allow another plugin to react before these things happen:&lt;br /&gt;
** course_will_be_restored (with info about course being restored and the target category)&lt;br /&gt;
** course_category_will_be_moved (with info about category being moved and target category)&lt;br /&gt;
** course_will_be_moved (with course info and target category info)&lt;br /&gt;
&lt;br /&gt;
Provide event trigger hooks for the modules, similar to what is done for the cron service which checks the LOCAL directory for a cron file. It is already possible to define an event trigger but the core/module code must be modified to actually make use of it.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [[Core APIs]]&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=69103 Original General Developer Forum thread discussing this proposal]. &lt;br /&gt;
* [[Messaging_2.0]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Coding guidelines|Events]]&lt;br /&gt;
[[Category:Grades]]&lt;br /&gt;
[[Category:API]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Local_plugins&amp;diff=41001</id>
		<title>Local plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Local_plugins&amp;diff=41001"/>
		<updated>2013-06-24T14:14:27Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Adding an element to the settings menu */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Local customisations&lt;br /&gt;
|state = Implemented&lt;br /&gt;
|tracker = MDL-17376, MDL-16438&lt;br /&gt;
|discussion = http://moodle.org/mod/forum/discuss.php?d=126017 http://moodle.org/mod/forum/discuss.php?d=86903&lt;br /&gt;
|assignee = [[User:Petr Škoda (škoďák)|Petr Škoda (škoďák)]], some parts were originally proposed and implemented in 1.9 by Penny Leach&lt;br /&gt;
}}&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The recommended way to add new functionality to Moodle is to create a new standard plugin (module, block, auth, enrol, etc.).The /local/ plugins are mostly suitable for things that do not fit standard plugins.&lt;br /&gt;
&lt;br /&gt;
== Custom /local/ plugins ==&lt;br /&gt;
&lt;br /&gt;
Local plugins are used in cases when no standard plugin fits, examples are:&lt;br /&gt;
* event consumers communicating with external systems&lt;br /&gt;
* custom definitions of web services and external functions&lt;br /&gt;
* applications that extend moodle at the system level (hub server, amos server, etc.)&lt;br /&gt;
* new database tables used in core hacks (discouraged)&lt;br /&gt;
* new capability definitions used in core hacks&lt;br /&gt;
* custom admin settings&lt;br /&gt;
* extending the navigation block with custom menus [http://moodle.org/mod/forum/discuss.php?d=170325&amp;amp;parent=753095]&lt;br /&gt;
&lt;br /&gt;
=== Standard plugin features: ===&lt;br /&gt;
* /local/xxx/[[version.php]] - version of script (must be incremented after changes)&lt;br /&gt;
* /local/xxx/db/install.xml - executed during install (new version.php found)&lt;br /&gt;
* /local/xxx/db/install.php - executed right after install.xml&lt;br /&gt;
* /local/xxx/db/uninstall.php - executed during uninstallation&lt;br /&gt;
* /local/xxx/db/upgrade.php - executed after version.php change&lt;br /&gt;
* /local/xxx/db/access.php - definition of capabilities&lt;br /&gt;
* /local/xxx/db/events.php - event handlers and subscripts&lt;br /&gt;
* /local/xxx/db/messages.php - messaging registration&lt;br /&gt;
* /local/xxx/db/external.php - web services and external functions descriptions&lt;br /&gt;
* /local/xxx/cron.php - cron job, run at the interval defined in version.php. Alternatively, you can define &amp;lt;tt&amp;gt;local_xxx_cron()&amp;lt;/tt&amp;gt; in lib.php. (The lib.php method is now preferred.)&lt;br /&gt;
* /local/xxx/lang/en/local_pluginname.php - language file&lt;br /&gt;
* /local/xxx/lib.php - function library, automatically included with by config.php.  Hook functions local_pluginname_extends_navigation() and local_pluginname_extends_settings_navigation() can be used to add items to the navigation and settings blocks&lt;br /&gt;
* /local/xxx/settings.php - configuration options. These get added to the admin menu.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;xxx&#039;&#039; is used instead of your local plugin name, plugins of the same type are installed/upgraded in alphabetical order.&lt;br /&gt;
&lt;br /&gt;
=== List of differences from normal plugins: ===&lt;br /&gt;
* always executed last during install/upgrade - guaranteed by order of plugins in &amp;lt;code&amp;gt;get_plugin_types()&amp;lt;/code&amp;gt;&lt;br /&gt;
* are expected to use event handlers - events are intended for communication core--&amp;gt;plugins only, local plugins are the best candidates for event handlers&lt;br /&gt;
* can add admin settings to any settings page - loaded last when constructing admin tree&lt;br /&gt;
* do not need to have any UI - other plugins are usually visible somewhere&lt;br /&gt;
* some extra hooks (not implemented yet)&lt;br /&gt;
&lt;br /&gt;
== /local/xxx/db/messages.php ==&lt;br /&gt;
Example File Structure:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Defines message providers (types of messages being sent)&lt;br /&gt;
 *&lt;br /&gt;
 * @package mod-forum&lt;br /&gt;
 * @copyright  1999 onwards  Martin Dougiamas  http://moodle.com&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;
$messageproviders = array (&lt;br /&gt;
&lt;br /&gt;
/// Ordinary single forum posts&lt;br /&gt;
    &#039;posts&#039; =&amp;gt; array (&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other /local/ customisation files== &lt;br /&gt;
&lt;br /&gt;
===Customised site defaults=== &lt;br /&gt;
&lt;br /&gt;
Different default site settings can be stored in file /local/defaults.php.&lt;br /&gt;
These new defaults are used during installation, upgrade and later are&lt;br /&gt;
displayed as default values in admin settings. This means that the content&lt;br /&gt;
of the defaults files is usually updated BEFORE installation or upgrade.&lt;br /&gt;
&lt;br /&gt;
These customised defaults are useful especially when using CLI tools&lt;br /&gt;
for installation and upgrade.&lt;br /&gt;
&lt;br /&gt;
Sample /local/defaults.php file content:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;forcelogin&#039;] = 1;  // new default for $CFG-&amp;gt;forcelogin&lt;br /&gt;
$defaults[&#039;scorm&#039;][&#039;maxgrade&#039;] = 20;    // default for get_config(&#039;scorm&#039;, &#039;maxgrade&#039;)&lt;br /&gt;
$defaults[&#039;moodlecourse&#039;][&#039;numsections&#039;] = 11;&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;hiddenuserfields&#039;] = array(&#039;city&#039;, &#039;country&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
First bracket contains string from column plugin of config_plugins table.&lt;br /&gt;
Second bracket is the name of setting. In the admin settings UI the plugin and&lt;br /&gt;
name of setting is separated by &amp;quot;|&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The values usually correspond to the raw string in config table, with the exception&lt;br /&gt;
of comma separated lists that are usually entered as real arrays.&lt;br /&gt;
&lt;br /&gt;
Please note that not all settings are converted to admin_tree,&lt;br /&gt;
they are mostly intended to be set directly in config.php.&lt;br /&gt;
&lt;br /&gt;
=== 2.0 pre-upgrade script===&lt;br /&gt;
&lt;br /&gt;
You can use /local/upgrade_pre20.php script for any code that needs to&lt;br /&gt;
be executed before the main upgrade to 2.0. Most probably this will&lt;br /&gt;
be used for undoing of old hacks that would otherwise break normal&lt;br /&gt;
2.0 upgrade.&lt;br /&gt;
&lt;br /&gt;
This file is just included directly, there does not need to be any&lt;br /&gt;
function inside. If the execution stops the script is executed again&lt;br /&gt;
during the next upgrade. The first execution of lib/db/upgrade.php&lt;br /&gt;
increments the version number and the pre upgrade script is not&lt;br /&gt;
executed any more.&lt;br /&gt;
&lt;br /&gt;
== Customisations outside of /local/ directory== &lt;br /&gt;
&lt;br /&gt;
=== Forced settings=== &lt;br /&gt;
&lt;br /&gt;
Sometimes it is useful to force some settings and prevent any changes of these settings via the standard admin UI. It is possible to hardcode these settings in config.php.&lt;br /&gt;
&lt;br /&gt;
In case of course settings it is very simply, the values are assigned directly to $CFG properties. In case of plugins the values are specified in a multidimensional array in $CFG-&amp;gt;force_plugin_settings.&lt;br /&gt;
&lt;br /&gt;
Sample code in config.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;allowobjectembed = 0;&lt;br /&gt;
$CFG-&amp;gt;forced_plugin_settings = array(&#039;page&#039;=&amp;gt;array(&#039;displayoptions&#039;=&amp;gt;5, &#039;requiremodintro&#039;=&amp;gt;1), &#039;folder&#039;=&amp;gt;array(&#039;requiremodintro&#039;=&amp;gt;1));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Local language customisations=== &lt;br /&gt;
&lt;br /&gt;
Moodle supports other type of local customisation of standard language&lt;br /&gt;
packs. If you want to create your own language pack based on another&lt;br /&gt;
language create new dataroot directory with &amp;quot;_local&amp;quot; suffix, for example&lt;br /&gt;
following file with content changes string &amp;quot;Login&amp;quot; to &amp;quot;Sign in&amp;quot;:&lt;br /&gt;
moodledata/lang/en_local&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
  $string[&#039;login&#039;] = &#039;Sign in&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
See also https://docs.moodle.org/en/Language_editing&lt;br /&gt;
&lt;br /&gt;
=== Custom script injection=== &lt;br /&gt;
&lt;br /&gt;
Very old customisation option that allows you to modify scripts by injecting&lt;br /&gt;
code right after the require &#039;config.php&#039; call.&lt;br /&gt;
&lt;br /&gt;
This setting is enabled by manually setting $CFG-&amp;gt;customscripts variable&lt;br /&gt;
in config.php script. The value is expected to be full path to directory&lt;br /&gt;
with the same structure as dirroot. Please note this hack only affects&lt;br /&gt;
files that actually include the config.php!&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
* disable one specific moodle page without code modification&lt;br /&gt;
* alter page parameters on the fly&lt;br /&gt;
&lt;br /&gt;
=== Direct code modifications=== &lt;br /&gt;
This is usually the last resort, if possible do not do it. And if you still do it use some version control system (preferably git).&lt;br /&gt;
&lt;br /&gt;
=== Direct database modifications=== &lt;br /&gt;
Very strongly discouraged! Sometimes field lengths may be modified without side effects. Adding or removing of db fields will most probably cause major problems during future upgrades. New database tables should be added only from plugins.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Adding an element to the settings menu ===&lt;br /&gt;
&lt;br /&gt;
In local/*pluginname*/lib.php, define a function named local_*pluginname*_extends_settings_navigation, this will get called when Moodle builds the settings block.&lt;br /&gt;
This example adds a link to the bottom of the course administration section of the settings block.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function local_myplugin_extends_settings_navigation($settingsnav, $context) {&lt;br /&gt;
    global $CFG, $PAGE;&lt;br /&gt;
&lt;br /&gt;
    // Only add this settings item on non-site course pages.&lt;br /&gt;
    if (!$PAGE-&amp;gt;course or $PAGE-&amp;gt;course-&amp;gt;id == 1) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Only let users with the appropriate capability see this settings item.&lt;br /&gt;
    if (!has_capability(&#039;moodle/backup:backupcourse&#039;, context_course::instance($PAGE-&amp;gt;course-&amp;gt;id))) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($settingnode = $settingsnav-&amp;gt;find(&#039;courseadmin&#039;, navigation_node::TYPE_COURSE)) {&lt;br /&gt;
        $strfoo = get_string(&#039;foo&#039;, &#039;local_myplugin&#039;);&lt;br /&gt;
        $url = new moodle_url(&#039;/local/myplugin/foo.php&#039;, array(&#039;id&#039; =&amp;gt; $PAGE-&amp;gt;course-&amp;gt;id));&lt;br /&gt;
        $foonode = navigation_node::create(&lt;br /&gt;
            $strfoo,&lt;br /&gt;
            $url,&lt;br /&gt;
            navigation_node::NODETYPE_LEAF,&lt;br /&gt;
            &#039;myplugin&#039;,&lt;br /&gt;
            &#039;myplugin&#039;,&lt;br /&gt;
            new pix_icon(&#039;t/addcontact&#039;, $strfoo)&lt;br /&gt;
        );&lt;br /&gt;
        if ($PAGE-&amp;gt;url-&amp;gt;compare($url, URL_MATCH_BASE)) {&lt;br /&gt;
            $foonode-&amp;gt;make_active();&lt;br /&gt;
        }&lt;br /&gt;
        $settingnode-&amp;gt;add_node($foonode);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local customisations in previous versions ==&lt;br /&gt;
Previous versions include only partial support for customisations in /local/ directory.&lt;br /&gt;
&lt;br /&gt;
=== List of local customisations in 1.9.x: ===&lt;br /&gt;
* /local/cron.php - custom cron jobs&lt;br /&gt;
* /local/settings.php - custom admin settings&lt;br /&gt;
* /local/db/upgrade.php - general modifications&lt;br /&gt;
* /local/lang/* - custom strings&lt;br /&gt;
* /local/lib.php - local_delete_course()&lt;br /&gt;
&lt;br /&gt;
=== Migration from old 1.9.x /local/: ===&lt;br /&gt;
* &amp;lt;code&amp;gt;local/*&amp;lt;/code&amp;gt; needs to be copied to new directory&lt;br /&gt;
* &amp;lt;code&amp;gt;local/xxxx/db/install.php&amp;lt;/code&amp;gt; is intended for first installation, originally everything was in upgrade.php&lt;br /&gt;
* events are used instead of hooks&lt;br /&gt;
* upgrade code needs to migrate old settings, events, etc. directly in core db tables - such as change component strings and capability names from db/install.php or manually before/after upgrade&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170325&amp;amp;parent=753095 Extending navigation block]&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=86903 Local Customisations] forum discussion&lt;br /&gt;
* http://cvs.moodle.org/moodle/local/readme.txt?view=markup&lt;br /&gt;
* [[Local customisation (Moodle 1.9)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Local_plugins&amp;diff=41000</id>
		<title>Local plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Local_plugins&amp;diff=41000"/>
		<updated>2013-06-24T14:10:25Z</updated>

		<summary type="html">&lt;p&gt;Brianking: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Local customisations&lt;br /&gt;
|state = Implemented&lt;br /&gt;
|tracker = MDL-17376, MDL-16438&lt;br /&gt;
|discussion = http://moodle.org/mod/forum/discuss.php?d=126017 http://moodle.org/mod/forum/discuss.php?d=86903&lt;br /&gt;
|assignee = [[User:Petr Škoda (škoďák)|Petr Škoda (škoďák)]], some parts were originally proposed and implemented in 1.9 by Penny Leach&lt;br /&gt;
}}&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The recommended way to add new functionality to Moodle is to create a new standard plugin (module, block, auth, enrol, etc.).The /local/ plugins are mostly suitable for things that do not fit standard plugins.&lt;br /&gt;
&lt;br /&gt;
== Custom /local/ plugins ==&lt;br /&gt;
&lt;br /&gt;
Local plugins are used in cases when no standard plugin fits, examples are:&lt;br /&gt;
* event consumers communicating with external systems&lt;br /&gt;
* custom definitions of web services and external functions&lt;br /&gt;
* applications that extend moodle at the system level (hub server, amos server, etc.)&lt;br /&gt;
* new database tables used in core hacks (discouraged)&lt;br /&gt;
* new capability definitions used in core hacks&lt;br /&gt;
* custom admin settings&lt;br /&gt;
* extending the navigation block with custom menus [http://moodle.org/mod/forum/discuss.php?d=170325&amp;amp;parent=753095]&lt;br /&gt;
&lt;br /&gt;
=== Standard plugin features: ===&lt;br /&gt;
* /local/xxx/[[version.php]] - version of script (must be incremented after changes)&lt;br /&gt;
* /local/xxx/db/install.xml - executed during install (new version.php found)&lt;br /&gt;
* /local/xxx/db/install.php - executed right after install.xml&lt;br /&gt;
* /local/xxx/db/uninstall.php - executed during uninstallation&lt;br /&gt;
* /local/xxx/db/upgrade.php - executed after version.php change&lt;br /&gt;
* /local/xxx/db/access.php - definition of capabilities&lt;br /&gt;
* /local/xxx/db/events.php - event handlers and subscripts&lt;br /&gt;
* /local/xxx/db/messages.php - messaging registration&lt;br /&gt;
* /local/xxx/db/external.php - web services and external functions descriptions&lt;br /&gt;
* /local/xxx/cron.php - cron job, run at the interval defined in version.php. Alternatively, you can define &amp;lt;tt&amp;gt;local_xxx_cron()&amp;lt;/tt&amp;gt; in lib.php. (The lib.php method is now preferred.)&lt;br /&gt;
* /local/xxx/lang/en/local_pluginname.php - language file&lt;br /&gt;
* /local/xxx/lib.php - function library, automatically included with by config.php.  Hook functions local_pluginname_extends_navigation() and local_pluginname_extends_settings_navigation() can be used to add items to the navigation and settings blocks&lt;br /&gt;
* /local/xxx/settings.php - configuration options. These get added to the admin menu.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;xxx&#039;&#039; is used instead of your local plugin name, plugins of the same type are installed/upgraded in alphabetical order.&lt;br /&gt;
&lt;br /&gt;
=== List of differences from normal plugins: ===&lt;br /&gt;
* always executed last during install/upgrade - guaranteed by order of plugins in &amp;lt;code&amp;gt;get_plugin_types()&amp;lt;/code&amp;gt;&lt;br /&gt;
* are expected to use event handlers - events are intended for communication core--&amp;gt;plugins only, local plugins are the best candidates for event handlers&lt;br /&gt;
* can add admin settings to any settings page - loaded last when constructing admin tree&lt;br /&gt;
* do not need to have any UI - other plugins are usually visible somewhere&lt;br /&gt;
* some extra hooks (not implemented yet)&lt;br /&gt;
&lt;br /&gt;
== /local/xxx/db/messages.php ==&lt;br /&gt;
Example File Structure:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Defines message providers (types of messages being sent)&lt;br /&gt;
 *&lt;br /&gt;
 * @package mod-forum&lt;br /&gt;
 * @copyright  1999 onwards  Martin Dougiamas  http://moodle.com&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;
$messageproviders = array (&lt;br /&gt;
&lt;br /&gt;
/// Ordinary single forum posts&lt;br /&gt;
    &#039;posts&#039; =&amp;gt; array (&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other /local/ customisation files== &lt;br /&gt;
&lt;br /&gt;
===Customised site defaults=== &lt;br /&gt;
&lt;br /&gt;
Different default site settings can be stored in file /local/defaults.php.&lt;br /&gt;
These new defaults are used during installation, upgrade and later are&lt;br /&gt;
displayed as default values in admin settings. This means that the content&lt;br /&gt;
of the defaults files is usually updated BEFORE installation or upgrade.&lt;br /&gt;
&lt;br /&gt;
These customised defaults are useful especially when using CLI tools&lt;br /&gt;
for installation and upgrade.&lt;br /&gt;
&lt;br /&gt;
Sample /local/defaults.php file content:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;forcelogin&#039;] = 1;  // new default for $CFG-&amp;gt;forcelogin&lt;br /&gt;
$defaults[&#039;scorm&#039;][&#039;maxgrade&#039;] = 20;    // default for get_config(&#039;scorm&#039;, &#039;maxgrade&#039;)&lt;br /&gt;
$defaults[&#039;moodlecourse&#039;][&#039;numsections&#039;] = 11;&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;hiddenuserfields&#039;] = array(&#039;city&#039;, &#039;country&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
First bracket contains string from column plugin of config_plugins table.&lt;br /&gt;
Second bracket is the name of setting. In the admin settings UI the plugin and&lt;br /&gt;
name of setting is separated by &amp;quot;|&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The values usually correspond to the raw string in config table, with the exception&lt;br /&gt;
of comma separated lists that are usually entered as real arrays.&lt;br /&gt;
&lt;br /&gt;
Please note that not all settings are converted to admin_tree,&lt;br /&gt;
they are mostly intended to be set directly in config.php.&lt;br /&gt;
&lt;br /&gt;
=== 2.0 pre-upgrade script===&lt;br /&gt;
&lt;br /&gt;
You can use /local/upgrade_pre20.php script for any code that needs to&lt;br /&gt;
be executed before the main upgrade to 2.0. Most probably this will&lt;br /&gt;
be used for undoing of old hacks that would otherwise break normal&lt;br /&gt;
2.0 upgrade.&lt;br /&gt;
&lt;br /&gt;
This file is just included directly, there does not need to be any&lt;br /&gt;
function inside. If the execution stops the script is executed again&lt;br /&gt;
during the next upgrade. The first execution of lib/db/upgrade.php&lt;br /&gt;
increments the version number and the pre upgrade script is not&lt;br /&gt;
executed any more.&lt;br /&gt;
&lt;br /&gt;
== Customisations outside of /local/ directory== &lt;br /&gt;
&lt;br /&gt;
=== Forced settings=== &lt;br /&gt;
&lt;br /&gt;
Sometimes it is useful to force some settings and prevent any changes of these settings via the standard admin UI. It is possible to hardcode these settings in config.php.&lt;br /&gt;
&lt;br /&gt;
In case of course settings it is very simply, the values are assigned directly to $CFG properties. In case of plugins the values are specified in a multidimensional array in $CFG-&amp;gt;force_plugin_settings.&lt;br /&gt;
&lt;br /&gt;
Sample code in config.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;allowobjectembed = 0;&lt;br /&gt;
$CFG-&amp;gt;forced_plugin_settings = array(&#039;page&#039;=&amp;gt;array(&#039;displayoptions&#039;=&amp;gt;5, &#039;requiremodintro&#039;=&amp;gt;1), &#039;folder&#039;=&amp;gt;array(&#039;requiremodintro&#039;=&amp;gt;1));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Local language customisations=== &lt;br /&gt;
&lt;br /&gt;
Moodle supports other type of local customisation of standard language&lt;br /&gt;
packs. If you want to create your own language pack based on another&lt;br /&gt;
language create new dataroot directory with &amp;quot;_local&amp;quot; suffix, for example&lt;br /&gt;
following file with content changes string &amp;quot;Login&amp;quot; to &amp;quot;Sign in&amp;quot;:&lt;br /&gt;
moodledata/lang/en_local&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
  $string[&#039;login&#039;] = &#039;Sign in&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
See also https://docs.moodle.org/en/Language_editing&lt;br /&gt;
&lt;br /&gt;
=== Custom script injection=== &lt;br /&gt;
&lt;br /&gt;
Very old customisation option that allows you to modify scripts by injecting&lt;br /&gt;
code right after the require &#039;config.php&#039; call.&lt;br /&gt;
&lt;br /&gt;
This setting is enabled by manually setting $CFG-&amp;gt;customscripts variable&lt;br /&gt;
in config.php script. The value is expected to be full path to directory&lt;br /&gt;
with the same structure as dirroot. Please note this hack only affects&lt;br /&gt;
files that actually include the config.php!&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
* disable one specific moodle page without code modification&lt;br /&gt;
* alter page parameters on the fly&lt;br /&gt;
&lt;br /&gt;
=== Direct code modifications=== &lt;br /&gt;
This is usually the last resort, if possible do not do it. And if you still do it use some version control system (preferably git).&lt;br /&gt;
&lt;br /&gt;
=== Direct database modifications=== &lt;br /&gt;
Very strongly discouraged! Sometimes field lengths may be modified without side effects. Adding or removing of db fields will most probably cause major problems during future upgrades. New database tables should be added only from plugins.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Adding an element to the settings menu ===&lt;br /&gt;
&lt;br /&gt;
In local/*pluginname*/lib.php, define a function named local_*pluginname*_extends_settings_navigation, this will get called when Moodle builds the settings block.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function local_myplugin_extends_settings_navigation($settingsnav, $context) {&lt;br /&gt;
    global $CFG, $PAGE;&lt;br /&gt;
&lt;br /&gt;
    // Only add this settings item on non-site course pages.&lt;br /&gt;
    if (!$PAGE-&amp;gt;course or $PAGE-&amp;gt;course-&amp;gt;id == 1) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Only let users with the appropriate capability see this settings item.&lt;br /&gt;
    if (!has_capability(&#039;moodle/backup:backupcourse&#039;, context_course::instance($PAGE-&amp;gt;course-&amp;gt;id))) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($settingnode = $settingsnav-&amp;gt;find(&#039;courseadmin&#039;, navigation_node::TYPE_COURSE)) {&lt;br /&gt;
        $strfoo = get_string(&#039;foo&#039;, &#039;local_myplugin&#039;);&lt;br /&gt;
        $url = new moodle_url(&#039;/local/myplugin/foo.php&#039;, array(&#039;id&#039; =&amp;gt; $PAGE-&amp;gt;course-&amp;gt;id));&lt;br /&gt;
        $foonode = navigation_node::create(&lt;br /&gt;
            $strfoo,&lt;br /&gt;
            $url,&lt;br /&gt;
            navigation_node::NODETYPE_LEAF,&lt;br /&gt;
            &#039;myplugin&#039;,&lt;br /&gt;
            &#039;myplugin&#039;,&lt;br /&gt;
            new pix_icon(&#039;t/addcontact&#039;, $strfoo)&lt;br /&gt;
        );&lt;br /&gt;
        if ($PAGE-&amp;gt;url-&amp;gt;compare($url, URL_MATCH_BASE)) {&lt;br /&gt;
            $foonode-&amp;gt;make_active();&lt;br /&gt;
        }&lt;br /&gt;
        $settingnode-&amp;gt;add_node($foonode);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Local customisations in previous versions ==&lt;br /&gt;
Previous versions include only partial support for customisations in /local/ directory.&lt;br /&gt;
&lt;br /&gt;
=== List of local customisations in 1.9.x: ===&lt;br /&gt;
* /local/cron.php - custom cron jobs&lt;br /&gt;
* /local/settings.php - custom admin settings&lt;br /&gt;
* /local/db/upgrade.php - general modifications&lt;br /&gt;
* /local/lang/* - custom strings&lt;br /&gt;
* /local/lib.php - local_delete_course()&lt;br /&gt;
&lt;br /&gt;
=== Migration from old 1.9.x /local/: ===&lt;br /&gt;
* &amp;lt;code&amp;gt;local/*&amp;lt;/code&amp;gt; needs to be copied to new directory&lt;br /&gt;
* &amp;lt;code&amp;gt;local/xxxx/db/install.php&amp;lt;/code&amp;gt; is intended for first installation, originally everything was in upgrade.php&lt;br /&gt;
* events are used instead of hooks&lt;br /&gt;
* upgrade code needs to migrate old settings, events, etc. directly in core db tables - such as change component strings and capability names from db/install.php or manually before/after upgrade&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170325&amp;amp;parent=753095 Extending navigation block]&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=86903 Local Customisations] forum discussion&lt;br /&gt;
* http://cvs.moodle.org/moodle/local/readme.txt?view=markup&lt;br /&gt;
* [[Local customisation (Moodle 1.9)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Local_plugins&amp;diff=40999</id>
		<title>Local plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Local_plugins&amp;diff=40999"/>
		<updated>2013-06-24T14:08:52Z</updated>

		<summary type="html">&lt;p&gt;Brianking: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Local customisations&lt;br /&gt;
|state = Implemented&lt;br /&gt;
|tracker = MDL-17376, MDL-16438&lt;br /&gt;
|discussion = http://moodle.org/mod/forum/discuss.php?d=126017 http://moodle.org/mod/forum/discuss.php?d=86903&lt;br /&gt;
|assignee = [[User:Petr Škoda (škoďák)|Petr Škoda (škoďák)]], some parts were originally proposed and implemented in 1.9 by Penny Leach&lt;br /&gt;
}}&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The recommended way to add new functionality to Moodle is to create a new standard plugin (module, block, auth, enrol, etc.).The /local/ plugins are mostly suitable for things that do not fit standard plugins.&lt;br /&gt;
&lt;br /&gt;
== Custom /local/ plugins ==&lt;br /&gt;
&lt;br /&gt;
Local plugins are used in cases when no standard plugin fits, examples are:&lt;br /&gt;
* event consumers communicating with external systems&lt;br /&gt;
* custom definitions of web services and external functions&lt;br /&gt;
* applications that extend moodle at the system level (hub server, amos server, etc.)&lt;br /&gt;
* new database tables used in core hacks (discouraged)&lt;br /&gt;
* new capability definitions used in core hacks&lt;br /&gt;
* custom admin settings&lt;br /&gt;
* extending the navigation block with custom menus [http://moodle.org/mod/forum/discuss.php?d=170325&amp;amp;parent=753095]&lt;br /&gt;
&lt;br /&gt;
=== Standard plugin features: ===&lt;br /&gt;
* /local/xxx/[[version.php]] - version of script (must be incremented after changes)&lt;br /&gt;
* /local/xxx/db/install.xml - executed during install (new version.php found)&lt;br /&gt;
* /local/xxx/db/install.php - executed right after install.xml&lt;br /&gt;
* /local/xxx/db/uninstall.php - executed during uninstallation&lt;br /&gt;
* /local/xxx/db/upgrade.php - executed after version.php change&lt;br /&gt;
* /local/xxx/db/access.php - definition of capabilities&lt;br /&gt;
* /local/xxx/db/events.php - event handlers and subscripts&lt;br /&gt;
* /local/xxx/db/messages.php - messaging registration&lt;br /&gt;
* /local/xxx/db/external.php - web services and external functions descriptions&lt;br /&gt;
* /local/xxx/cron.php - cron job, run at the interval defined in version.php. Alternatively, you can define &amp;lt;tt&amp;gt;local_xxx_cron()&amp;lt;/tt&amp;gt; in lib.php. (The lib.php method is now preferred.)&lt;br /&gt;
* /local/xxx/lang/en/local_pluginname.php - language file&lt;br /&gt;
* /local/xxx/lib.php - function library, automatically included with by config.php.  Hook functions local_pluginname_extends_navigation() and local_pluginname_extends_settings_navigation() can be used to add items to the navigation and settings blocks&lt;br /&gt;
* /local/xxx/settings.php - configuration options. These get added to the admin menu.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;xxx&#039;&#039; is used instead of your local plugin name, plugins of the same type are installed/upgraded in alphabetical order.&lt;br /&gt;
&lt;br /&gt;
=== List of differences from normal plugins: ===&lt;br /&gt;
* always executed last during install/upgrade - guaranteed by order of plugins in &amp;lt;code&amp;gt;get_plugin_types()&amp;lt;/code&amp;gt;&lt;br /&gt;
* are expected to use event handlers - events are intended for communication core--&amp;gt;plugins only, local plugins are the best candidates for event handlers&lt;br /&gt;
* can add admin settings to any settings page - loaded last when constructing admin tree&lt;br /&gt;
* do not need to have any UI - other plugins are usually visible somewhere&lt;br /&gt;
* some extra hooks (not implemented yet)&lt;br /&gt;
&lt;br /&gt;
== /local/xxx/db/messages.php ==&lt;br /&gt;
Example File Structure:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Defines message providers (types of messages being sent)&lt;br /&gt;
 *&lt;br /&gt;
 * @package mod-forum&lt;br /&gt;
 * @copyright  1999 onwards  Martin Dougiamas  http://moodle.com&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;
$messageproviders = array (&lt;br /&gt;
&lt;br /&gt;
/// Ordinary single forum posts&lt;br /&gt;
    &#039;posts&#039; =&amp;gt; array (&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other /local/ customisation files== &lt;br /&gt;
&lt;br /&gt;
===Customised site defaults=== &lt;br /&gt;
&lt;br /&gt;
Different default site settings can be stored in file /local/defaults.php.&lt;br /&gt;
These new defaults are used during installation, upgrade and later are&lt;br /&gt;
displayed as default values in admin settings. This means that the content&lt;br /&gt;
of the defaults files is usually updated BEFORE installation or upgrade.&lt;br /&gt;
&lt;br /&gt;
These customised defaults are useful especially when using CLI tools&lt;br /&gt;
for installation and upgrade.&lt;br /&gt;
&lt;br /&gt;
Sample /local/defaults.php file content:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;forcelogin&#039;] = 1;  // new default for $CFG-&amp;gt;forcelogin&lt;br /&gt;
$defaults[&#039;scorm&#039;][&#039;maxgrade&#039;] = 20;    // default for get_config(&#039;scorm&#039;, &#039;maxgrade&#039;)&lt;br /&gt;
$defaults[&#039;moodlecourse&#039;][&#039;numsections&#039;] = 11;&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;hiddenuserfields&#039;] = array(&#039;city&#039;, &#039;country&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
First bracket contains string from column plugin of config_plugins table.&lt;br /&gt;
Second bracket is the name of setting. In the admin settings UI the plugin and&lt;br /&gt;
name of setting is separated by &amp;quot;|&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The values usually correspond to the raw string in config table, with the exception&lt;br /&gt;
of comma separated lists that are usually entered as real arrays.&lt;br /&gt;
&lt;br /&gt;
Please note that not all settings are converted to admin_tree,&lt;br /&gt;
they are mostly intended to be set directly in config.php.&lt;br /&gt;
&lt;br /&gt;
=== 2.0 pre-upgrade script===&lt;br /&gt;
&lt;br /&gt;
You can use /local/upgrade_pre20.php script for any code that needs to&lt;br /&gt;
be executed before the main upgrade to 2.0. Most probably this will&lt;br /&gt;
be used for undoing of old hacks that would otherwise break normal&lt;br /&gt;
2.0 upgrade.&lt;br /&gt;
&lt;br /&gt;
This file is just included directly, there does not need to be any&lt;br /&gt;
function inside. If the execution stops the script is executed again&lt;br /&gt;
during the next upgrade. The first execution of lib/db/upgrade.php&lt;br /&gt;
increments the version number and the pre upgrade script is not&lt;br /&gt;
executed any more.&lt;br /&gt;
&lt;br /&gt;
== Customisations outside of /local/ directory== &lt;br /&gt;
&lt;br /&gt;
=== Forced settings=== &lt;br /&gt;
&lt;br /&gt;
Sometimes it is useful to force some settings and prevent any changes of these settings via the standard admin UI. It is possible to hardcode these settings in config.php.&lt;br /&gt;
&lt;br /&gt;
In case of course settings it is very simply, the values are assigned directly to $CFG properties. In case of plugins the values are specified in a multidimensional array in $CFG-&amp;gt;force_plugin_settings.&lt;br /&gt;
&lt;br /&gt;
Sample code in config.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;allowobjectembed = 0;&lt;br /&gt;
$CFG-&amp;gt;forced_plugin_settings = array(&#039;page&#039;=&amp;gt;array(&#039;displayoptions&#039;=&amp;gt;5, &#039;requiremodintro&#039;=&amp;gt;1), &#039;folder&#039;=&amp;gt;array(&#039;requiremodintro&#039;=&amp;gt;1));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Local language customisations=== &lt;br /&gt;
&lt;br /&gt;
Moodle supports other type of local customisation of standard language&lt;br /&gt;
packs. If you want to create your own language pack based on another&lt;br /&gt;
language create new dataroot directory with &amp;quot;_local&amp;quot; suffix, for example&lt;br /&gt;
following file with content changes string &amp;quot;Login&amp;quot; to &amp;quot;Sign in&amp;quot;:&lt;br /&gt;
moodledata/lang/en_local&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
  $string[&#039;login&#039;] = &#039;Sign in&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
See also https://docs.moodle.org/en/Language_editing&lt;br /&gt;
&lt;br /&gt;
=== Custom script injection=== &lt;br /&gt;
&lt;br /&gt;
Very old customisation option that allows you to modify scripts by injecting&lt;br /&gt;
code right after the require &#039;config.php&#039; call.&lt;br /&gt;
&lt;br /&gt;
This setting is enabled by manually setting $CFG-&amp;gt;customscripts variable&lt;br /&gt;
in config.php script. The value is expected to be full path to directory&lt;br /&gt;
with the same structure as dirroot. Please note this hack only affects&lt;br /&gt;
files that actually include the config.php!&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
* disable one specific moodle page without code modification&lt;br /&gt;
* alter page parameters on the fly&lt;br /&gt;
&lt;br /&gt;
=== Direct code modifications=== &lt;br /&gt;
This is usually the last resort, if possible do not do it. And if you still do it use some version control system (preferably git).&lt;br /&gt;
&lt;br /&gt;
=== Direct database modifications=== &lt;br /&gt;
Very strongly discouraged! Sometimes field lengths may be modified without side effects. Adding or removing of db fields will most probably cause major problems during future upgrades. New database tables should be added only from plugins.&lt;br /&gt;
&lt;br /&gt;
== Examples ==&lt;br /&gt;
&lt;br /&gt;
=== Adding an element to the settings menu ===&lt;br /&gt;
&lt;br /&gt;
In local/*pluginname*/lib.php, define a function named local_*pluginname*_extends_settings_navigation, this will get called when Moodle builds the settings block.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
function local_myplugin_extends_settings_navigation($settingsnav, $context) {&lt;br /&gt;
    global $CFG, $PAGE;&lt;br /&gt;
&lt;br /&gt;
    // Only add this settings item on non-site course pages.&lt;br /&gt;
    if (!$PAGE-&amp;gt;course or $PAGE-&amp;gt;course-&amp;gt;id == 1) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    // Only let users with the appropriate capability see this settings item.&lt;br /&gt;
    if (!has_capability(&#039;moodle/backup:backupcourse&#039;, context_course::instance($PAGE-&amp;gt;course-&amp;gt;id))) {&lt;br /&gt;
        return;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
    if ($settingnode = $settingsnav-&amp;gt;find(&#039;courseadmin&#039;, navigation_node::TYPE_COURSE)) {&lt;br /&gt;
        $strfoo = get_string(&#039;foo&#039;, &#039;local_myplugin&#039;);&lt;br /&gt;
        $url = new moodle_url(&#039;/local/myplugin/foo.php&#039;, array(&#039;id&#039; =&amp;gt; $PAGE-&amp;gt;course-&amp;gt;id));&lt;br /&gt;
        $foonode = navigation_node::create(&lt;br /&gt;
            $strfoo,&lt;br /&gt;
            $url,&lt;br /&gt;
            navigation_node::NODETYPE_LEAF,&lt;br /&gt;
            &#039;myplugin&#039;,&lt;br /&gt;
            &#039;myplugin&#039;,&lt;br /&gt;
            new pix_icon(&#039;t/addcontact&#039;, $strfoo)&lt;br /&gt;
        );&lt;br /&gt;
        if ($PAGE-&amp;gt;url-&amp;gt;compare($url, URL_MATCH_BASE)) {&lt;br /&gt;
            $foonode-&amp;gt;make_active();&lt;br /&gt;
        }&lt;br /&gt;
        $settingnode-&amp;gt;add_node($foonode);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Local customisations in previous versions ==&lt;br /&gt;
Previous versions include only partial support for customisations in /local/ directory.&lt;br /&gt;
&lt;br /&gt;
=== List of local customisations in 1.9.x: ===&lt;br /&gt;
* /local/cron.php - custom cron jobs&lt;br /&gt;
* /local/settings.php - custom admin settings&lt;br /&gt;
* /local/db/upgrade.php - general modifications&lt;br /&gt;
* /local/lang/* - custom strings&lt;br /&gt;
* /local/lib.php - local_delete_course()&lt;br /&gt;
&lt;br /&gt;
=== Migration from old 1.9.x /local/: ===&lt;br /&gt;
* &amp;lt;code&amp;gt;local/*&amp;lt;/code&amp;gt; needs to be copied to new directory&lt;br /&gt;
* &amp;lt;code&amp;gt;local/xxxx/db/install.php&amp;lt;/code&amp;gt; is intended for first installation, originally everything was in upgrade.php&lt;br /&gt;
* events are used instead of hooks&lt;br /&gt;
* upgrade code needs to migrate old settings, events, etc. directly in core db tables - such as change component strings and capability names from db/install.php or manually before/after upgrade&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170325&amp;amp;parent=753095 Extending navigation block]&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=86903 Local Customisations] forum discussion&lt;br /&gt;
* http://cvs.moodle.org/moodle/local/readme.txt?view=markup&lt;br /&gt;
* [[Local customisation (Moodle 1.9)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Local_plugins&amp;diff=40998</id>
		<title>Local plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Local_plugins&amp;diff=40998"/>
		<updated>2013-06-24T13:47:28Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Standard plugin features: */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Local customisations&lt;br /&gt;
|state = Implemented&lt;br /&gt;
|tracker = MDL-17376, MDL-16438&lt;br /&gt;
|discussion = http://moodle.org/mod/forum/discuss.php?d=126017 http://moodle.org/mod/forum/discuss.php?d=86903&lt;br /&gt;
|assignee = [[User:Petr Škoda (škoďák)|Petr Škoda (škoďák)]], some parts were originally proposed and implemented in 1.9 by Penny Leach&lt;br /&gt;
}}&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
The recommended way to add new functionality to Moodle is to create a new standard plugin (module, block, auth, enrol, etc.).The /local/ plugins are mostly suitable for things that do not fit standard plugins.&lt;br /&gt;
&lt;br /&gt;
== Custom /local/ plugins ==&lt;br /&gt;
&lt;br /&gt;
Local plugins are used in cases when no standard plugin fits, examples are:&lt;br /&gt;
* event consumers communicating with external systems&lt;br /&gt;
* custom definitions of web services and external functions&lt;br /&gt;
* applications that extend moodle at the system level (hub server, amos server, etc.)&lt;br /&gt;
* new database tables used in core hacks (discouraged)&lt;br /&gt;
* new capability definitions used in core hacks&lt;br /&gt;
* custom admin settings&lt;br /&gt;
* extending the navigation block with custom menus [http://moodle.org/mod/forum/discuss.php?d=170325&amp;amp;parent=753095]&lt;br /&gt;
&lt;br /&gt;
=== Standard plugin features: ===&lt;br /&gt;
* /local/xxx/[[version.php]] - version of script (must be incremented after changes)&lt;br /&gt;
* /local/xxx/db/install.xml - executed during install (new version.php found)&lt;br /&gt;
* /local/xxx/db/install.php - executed right after install.xml&lt;br /&gt;
* /local/xxx/db/uninstall.php - executed during uninstallation&lt;br /&gt;
* /local/xxx/db/upgrade.php - executed after version.php change&lt;br /&gt;
* /local/xxx/db/access.php - definition of capabilities&lt;br /&gt;
* /local/xxx/db/events.php - event handlers and subscripts&lt;br /&gt;
* /local/xxx/db/messages.php - messaging registration&lt;br /&gt;
* /local/xxx/db/external.php - web services and external functions descriptions&lt;br /&gt;
* /local/xxx/cron.php - cron job, run at the interval defined in version.php. Alternatively, you can define &amp;lt;tt&amp;gt;local_xxx_cron()&amp;lt;/tt&amp;gt; in lib.php. (The lib.php method is now preferred.)&lt;br /&gt;
* /local/xxx/lang/en/local_pluginname.php - language file&lt;br /&gt;
* /local/xxx/lib.php - function library, automatically included with by config.php.  Hook functions local_pluginname_extends_navigation() and local_pluginname_extends_settings_navigation() can be used to add items to the navigation and settings blocks&lt;br /&gt;
* /local/xxx/settings.php - configuration options. These get added to the admin menu.&lt;br /&gt;
&lt;br /&gt;
The &#039;&#039;xxx&#039;&#039; is used instead of your local plugin name, plugins of the same type are installed/upgraded in alphabetical order.&lt;br /&gt;
&lt;br /&gt;
=== List of differences from normal plugins: ===&lt;br /&gt;
* always executed last during install/upgrade - guaranteed by order of plugins in &amp;lt;code&amp;gt;get_plugin_types()&amp;lt;/code&amp;gt;&lt;br /&gt;
* are expected to use event handlers - events are intended for communication core--&amp;gt;plugins only, local plugins are the best candidates for event handlers&lt;br /&gt;
* can add admin settings to any settings page - loaded last when constructing admin tree&lt;br /&gt;
* do not need to have any UI - other plugins are usually visible somewhere&lt;br /&gt;
* some extra hooks (not implemented yet)&lt;br /&gt;
&lt;br /&gt;
== /local/xxx/db/messages.php ==&lt;br /&gt;
Example File Structure:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
/**&lt;br /&gt;
 * Defines message providers (types of messages being sent)&lt;br /&gt;
 *&lt;br /&gt;
 * @package mod-forum&lt;br /&gt;
 * @copyright  1999 onwards  Martin Dougiamas  http://moodle.com&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;
$messageproviders = array (&lt;br /&gt;
&lt;br /&gt;
/// Ordinary single forum posts&lt;br /&gt;
    &#039;posts&#039; =&amp;gt; array (&lt;br /&gt;
    )&lt;br /&gt;
&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Other /local/ customisation files== &lt;br /&gt;
&lt;br /&gt;
===Customised site defaults=== &lt;br /&gt;
&lt;br /&gt;
Different default site settings can be stored in file /local/defaults.php.&lt;br /&gt;
These new defaults are used during installation, upgrade and later are&lt;br /&gt;
displayed as default values in admin settings. This means that the content&lt;br /&gt;
of the defaults files is usually updated BEFORE installation or upgrade.&lt;br /&gt;
&lt;br /&gt;
These customised defaults are useful especially when using CLI tools&lt;br /&gt;
for installation and upgrade.&lt;br /&gt;
&lt;br /&gt;
Sample /local/defaults.php file content:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;forcelogin&#039;] = 1;  // new default for $CFG-&amp;gt;forcelogin&lt;br /&gt;
$defaults[&#039;scorm&#039;][&#039;maxgrade&#039;] = 20;    // default for get_config(&#039;scorm&#039;, &#039;maxgrade&#039;)&lt;br /&gt;
$defaults[&#039;moodlecourse&#039;][&#039;numsections&#039;] = 11;&lt;br /&gt;
$defaults[&#039;moodle&#039;][&#039;hiddenuserfields&#039;] = array(&#039;city&#039;, &#039;country&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
First bracket contains string from column plugin of config_plugins table.&lt;br /&gt;
Second bracket is the name of setting. In the admin settings UI the plugin and&lt;br /&gt;
name of setting is separated by &amp;quot;|&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
The values usually correspond to the raw string in config table, with the exception&lt;br /&gt;
of comma separated lists that are usually entered as real arrays.&lt;br /&gt;
&lt;br /&gt;
Please note that not all settings are converted to admin_tree,&lt;br /&gt;
they are mostly intended to be set directly in config.php.&lt;br /&gt;
&lt;br /&gt;
=== 2.0 pre-upgrade script===&lt;br /&gt;
&lt;br /&gt;
You can use /local/upgrade_pre20.php script for any code that needs to&lt;br /&gt;
be executed before the main upgrade to 2.0. Most probably this will&lt;br /&gt;
be used for undoing of old hacks that would otherwise break normal&lt;br /&gt;
2.0 upgrade.&lt;br /&gt;
&lt;br /&gt;
This file is just included directly, there does not need to be any&lt;br /&gt;
function inside. If the execution stops the script is executed again&lt;br /&gt;
during the next upgrade. The first execution of lib/db/upgrade.php&lt;br /&gt;
increments the version number and the pre upgrade script is not&lt;br /&gt;
executed any more.&lt;br /&gt;
&lt;br /&gt;
== Customisations outside of /local/ directory== &lt;br /&gt;
&lt;br /&gt;
=== Forced settings=== &lt;br /&gt;
&lt;br /&gt;
Sometimes it is useful to force some settings and prevent any changes of these settings via the standard admin UI. It is possible to hardcode these settings in config.php.&lt;br /&gt;
&lt;br /&gt;
In case of course settings it is very simply, the values are assigned directly to $CFG properties. In case of plugins the values are specified in a multidimensional array in $CFG-&amp;gt;force_plugin_settings.&lt;br /&gt;
&lt;br /&gt;
Sample code in config.php&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$CFG-&amp;gt;allowobjectembed = 0;&lt;br /&gt;
$CFG-&amp;gt;forced_plugin_settings = array(&#039;page&#039;=&amp;gt;array(&#039;displayoptions&#039;=&amp;gt;5, &#039;requiremodintro&#039;=&amp;gt;1), &#039;folder&#039;=&amp;gt;array(&#039;requiremodintro&#039;=&amp;gt;1));&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Local language customisations=== &lt;br /&gt;
&lt;br /&gt;
Moodle supports other type of local customisation of standard language&lt;br /&gt;
packs. If you want to create your own language pack based on another&lt;br /&gt;
language create new dataroot directory with &amp;quot;_local&amp;quot; suffix, for example&lt;br /&gt;
following file with content changes string &amp;quot;Login&amp;quot; to &amp;quot;Sign in&amp;quot;:&lt;br /&gt;
moodledata/lang/en_local&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
  $string[&#039;login&#039;] = &#039;Sign in&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
See also https://docs.moodle.org/en/Language_editing&lt;br /&gt;
&lt;br /&gt;
=== Custom script injection=== &lt;br /&gt;
&lt;br /&gt;
Very old customisation option that allows you to modify scripts by injecting&lt;br /&gt;
code right after the require &#039;config.php&#039; call.&lt;br /&gt;
&lt;br /&gt;
This setting is enabled by manually setting $CFG-&amp;gt;customscripts variable&lt;br /&gt;
in config.php script. The value is expected to be full path to directory&lt;br /&gt;
with the same structure as dirroot. Please note this hack only affects&lt;br /&gt;
files that actually include the config.php!&lt;br /&gt;
&lt;br /&gt;
; Examples:&lt;br /&gt;
* disable one specific moodle page without code modification&lt;br /&gt;
* alter page parameters on the fly&lt;br /&gt;
&lt;br /&gt;
=== Direct code modifications=== &lt;br /&gt;
This is usually the last resort, if possible do not do it. And if you still do it use some version control system (preferably git).&lt;br /&gt;
&lt;br /&gt;
=== Direct database modifications=== &lt;br /&gt;
Very strongly discouraged! Sometimes field lengths may be modified without side effects. Adding or removing of db fields will most probably cause major problems during future upgrades. New database tables should be added only from plugins.&lt;br /&gt;
&lt;br /&gt;
== Local customisations in previous versions ==&lt;br /&gt;
Previous versions include only partial support for customisations in /local/ directory.&lt;br /&gt;
&lt;br /&gt;
=== List of local customisations in 1.9.x: ===&lt;br /&gt;
* /local/cron.php - custom cron jobs&lt;br /&gt;
* /local/settings.php - custom admin settings&lt;br /&gt;
* /local/db/upgrade.php - general modifications&lt;br /&gt;
* /local/lang/* - custom strings&lt;br /&gt;
* /local/lib.php - local_delete_course()&lt;br /&gt;
&lt;br /&gt;
=== Migration from old 1.9.x /local/: ===&lt;br /&gt;
* &amp;lt;code&amp;gt;local/*&amp;lt;/code&amp;gt; needs to be copied to new directory&lt;br /&gt;
* &amp;lt;code&amp;gt;local/xxxx/db/install.php&amp;lt;/code&amp;gt; is intended for first installation, originally everything was in upgrade.php&lt;br /&gt;
* events are used instead of hooks&lt;br /&gt;
* upgrade code needs to migrate old settings, events, etc. directly in core db tables - such as change component strings and capability names from db/install.php or manually before/after upgrade&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=170325&amp;amp;parent=753095 Extending navigation block]&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=86903 Local Customisations] forum discussion&lt;br /&gt;
* http://cvs.moodle.org/moodle/local/readme.txt?view=markup&lt;br /&gt;
* [[Local customisation (Moodle 1.9)]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6563</id>
		<title>Old Events API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6563"/>
		<updated>2010-05-04T11:42:04Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Handling an event */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Overview==&lt;br /&gt;
&lt;br /&gt;
The Events API is a core system in Moodle to allow communication between modules.  &lt;br /&gt;
&lt;br /&gt;
An &#039;&#039;&#039;event&#039;&#039;&#039; is when something &amp;quot;interesting&amp;quot; happens in Moodle that is worth alerting the system about.&lt;br /&gt;
&lt;br /&gt;
Any Moodle modules can &#039;&#039;&#039;trigger&#039;&#039;&#039; new events (with attached data), and other modules can elect to &#039;&#039;&#039;handle&#039;&#039;&#039; those events with custom functions that operate on the given data.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s look at an example of how events are used to implement Messaging in Moodle 2.0.  In the messaging system, textual messages are generated for users by different modules, and the user can decide how certain types of messages are displayed.&lt;br /&gt;
&lt;br /&gt;
===Triggering an event===&lt;br /&gt;
&lt;br /&gt;
When a messaging event occurs, the module should trigger a &amp;quot;message_send&amp;quot; event.   In this example let&#039;s pretend someone just posted to a forum.&lt;br /&gt;
&lt;br /&gt;
The forum module needs to create an object with the data that this event needs.  This may vary completely for different types of events, it&#039;s just a data object.&lt;br /&gt;
&lt;br /&gt;
 $eventdata = new object();&lt;br /&gt;
 $eventdata-&amp;gt;component         = &#039;mod/forum&#039;;    // path in Moodle&lt;br /&gt;
 $eventdata-&amp;gt;name              = &#039;posts&#039;;        // type of message from that module (as module defines it)&lt;br /&gt;
 $eventdata-&amp;gt;userfrom          = $userfrom;      // user object&lt;br /&gt;
 $eventdata-&amp;gt;userto            = $userto;        // user object&lt;br /&gt;
 $eventdata-&amp;gt;subject           = &amp;quot;Hi there&amp;quot;;     // short one-line subject&lt;br /&gt;
 $eventdata-&amp;gt;fullmessage       = &amp;quot;Here is the full message&amp;quot;;      // raw text&lt;br /&gt;
 $eventdata-&amp;gt;fullmessageformat = FORMAT_PLAIN;   // text format&lt;br /&gt;
 $eventdata-&amp;gt;fullmessagehtml   = &amp;quot;Here is the &amp;amp;lt;b&amp;gt;full&amp;amp;lt;/b&amp;gt; message&amp;quot;;    // html rendered version   (optional)&lt;br /&gt;
 $eventdata-&amp;gt;smallmessage      = &amp;quot;Here is the truncated message&amp;quot;;      // useful for plugins like sms or twitter  (optional)&lt;br /&gt;
&lt;br /&gt;
Then we post the object as an event and forget about it:&lt;br /&gt;
&lt;br /&gt;
 events_trigger(&#039;message_send&#039;, $eventdata);&lt;br /&gt;
&lt;br /&gt;
===Handling an event===&lt;br /&gt;
&lt;br /&gt;
Modules or core code can define an events.php in the db directory which defines events they want to be notified about, and describes which of their functions or class methods should be notified.  For this case, there is this definition of a handler in lib/db/events.php&lt;br /&gt;
&lt;br /&gt;
 $handlers = array (&lt;br /&gt;
     &#039;message_send&#039; =&amp;gt; array (&lt;br /&gt;
          &#039;handlerfile&#039;      =&amp;gt; &#039;/lib/messagelib.php&#039;,&lt;br /&gt;
          &#039;handlerfunction&#039;  =&amp;gt; &#039;message_send_handler&#039;,&lt;br /&gt;
          &#039;schedule&#039;         =&amp;gt; &#039;instant&#039;&lt;br /&gt;
      )&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
These events.php files are parsed during install / upgrade and stored in a simple database table.&lt;br /&gt;
&lt;br /&gt;
Now, when a &#039;&#039;&#039;message_send&#039;&#039;&#039; event happens, all the registered handlers functions for that event will be called something like this (but with more error handling):&lt;br /&gt;
&lt;br /&gt;
          include_once($CFG-&amp;gt;dirroot.$handlers[&#039;message_send&#039;][&#039;handlerfile&#039;]);&lt;br /&gt;
          call_user_func($handlers[&#039;message_send&#039;][&#039;handlerfunction&#039;], $eventdata);&lt;br /&gt;
&lt;br /&gt;
Any code can hook into any events this way.&lt;br /&gt;
&lt;br /&gt;
The handler function accepts one parameter (the event data object) and should return a boolean.  Returning false indicates that there was an error and the event will be left in the event queue.&lt;br /&gt;
&lt;br /&gt;
    function message_send_handler($eventdata) {&lt;br /&gt;
        // handle event &lt;br /&gt;
        // ...&lt;br /&gt;
        return true;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
==Database structure==&lt;br /&gt;
&lt;br /&gt;
There are 3 core tables for events. Note that if a handler is queued, and yet to be processed or processing failed, then all subsequent calls on that handler must be queued.&lt;br /&gt;
&lt;br /&gt;
===events_handlers===&lt;br /&gt;
&lt;br /&gt;
This table is for storing which components requests what type of event, and the location of the responsible handler functions.&lt;br /&gt;
&lt;br /&gt;
These entries are created by parsing events.php files in all the modules, and can be rebuilt any time (during an upgrade, say).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventname&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of the event, e.g. &#039;message_send&#039;&lt;br /&gt;
|-&lt;br /&gt;
|handlermodule&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|e.g. moodle, mod/forum, block/rss_client&lt;br /&gt;
|-&lt;br /&gt;
|handlerfile&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|path to the file of the function, eg /lib/messagelib.php&lt;br /&gt;
|-&lt;br /&gt;
|handlerfunction&lt;br /&gt;
|text&lt;br /&gt;
|serialized string or array describing function, suitable to be passed to &#039;&#039;&#039;call_user_func()&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|schedule 	&lt;br /&gt;
|varchar(255) 	&lt;br /&gt;
|&#039;cron&#039; or &#039;instant&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue===&lt;br /&gt;
&lt;br /&gt;
This table is for storing queued events. It stores only one copy of the eventdata here, and entries from this table are being references by the events_queue_handlers table.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventdata 	&lt;br /&gt;
|longtext 	&lt;br /&gt;
|serialized version of the data object passed to the event handler.&lt;br /&gt;
|-&lt;br /&gt;
|stackdump&lt;br /&gt;
|text&lt;br /&gt;
|serialized debug_backtrace showing where the event was fired from&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|int(10)&lt;br /&gt;
|$USER-&amp;gt;id when the event was fired&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) 	&lt;br /&gt;
|time stamp of the first time this was added&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue_handlers===&lt;br /&gt;
&lt;br /&gt;
This is the list of queued handlers for processing. The event object is retrieved from the events_queue table. When no further reference is made to the events_queue table, the corresponding entry in the events_queue table should be deleted. Entry should get deleted (?) after a successful event processing by the specified handler.  The status field keeps track of failures, after it gets to a certain number (eg 10?) it should trigger an &amp;quot;event failed&amp;quot; event (that could result in admin being emailed etc, or perhaps even the originating module taking care of it or rolling something back etc).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|queuedeventid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_queues table&lt;br /&gt;
|-&lt;br /&gt;
|handlerid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_handlers table&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|-&lt;br /&gt;
|errormessage&lt;br /&gt;
|text&lt;br /&gt;
|if an error happened last time we tried to process this event, record it here.&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|time stamp of the last attempt to run this from the queue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Standards for naming events==&lt;br /&gt;
&lt;br /&gt;
All event names should follow a consistent naming pattern, such as modulename_noun_verb&lt;br /&gt;
&lt;br /&gt;
If the event is being fired after the action has taken place (as in most cases) then use the past tense for the verb (created / deleted / updated / sent).&lt;br /&gt;
&lt;br /&gt;
If the event &#039;&#039;&#039;is&#039;&#039;&#039; the action, then use the present tense (create / delete / update / send).&lt;br /&gt;
&lt;br /&gt;
==Events which exist==&lt;br /&gt;
&lt;br /&gt;
When we add new events to core we should always add them here too.&lt;br /&gt;
&lt;br /&gt;
Under each event, list the data sent as part of the event.&lt;br /&gt;
&lt;br /&gt;
===Users===&lt;br /&gt;
* user_created&lt;br /&gt;
** full new record from &#039;user&#039; table&lt;br /&gt;
* user_updated&lt;br /&gt;
** full new record from &#039;user&#039; table&lt;br /&gt;
&lt;br /&gt;
===Courses===&lt;br /&gt;
* course_created&lt;br /&gt;
** full course record&lt;br /&gt;
* course_updated&lt;br /&gt;
** full course record&lt;br /&gt;
* course_deleted&lt;br /&gt;
** full course record&lt;br /&gt;
* course_category_deleted&lt;br /&gt;
** full category record&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
* groups_member_added&lt;br /&gt;
** groupid&lt;br /&gt;
** userid&lt;br /&gt;
* groups_member_removed&lt;br /&gt;
** groupid&lt;br /&gt;
** userid&lt;br /&gt;
* groups_group_created&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** description&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
** picture&lt;br /&gt;
* groups_group_updated&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** description&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
** picture&lt;br /&gt;
* groups_group_deleted&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** description&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
** picture&lt;br /&gt;
* groups_grouping_created&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
* groups_grouping_updated&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
* groups_grouping_deleted&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
* groups_members_removed (user deleted from all groups in a course)&lt;br /&gt;
** courseid&lt;br /&gt;
** userid&lt;br /&gt;
* groups_groupings_groups_removed (remove all groups from all groupings in a course)&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
* groups_groups_deleted (delete all groups in a course)&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
* groups_groupings_deleted (delete all groupings in a course)&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
&lt;br /&gt;
===Messaging===&lt;br /&gt;
* message_send&lt;br /&gt;
** component = &#039;mod/forum&#039;: path in Moodle&lt;br /&gt;
** name = &#039;posts&#039;: type of message from that module (as module defines it)&lt;br /&gt;
** userfrom = $userfrom: a user object to send from&lt;br /&gt;
** userto = $userto: a user object to send to&lt;br /&gt;
** subject = &#039;subject line&#039;: a short text line&lt;br /&gt;
** fullmessage = &#039;full plain text&#039;: raw text as entered by user&lt;br /&gt;
** fullmessageformat = FORMAT_PLAIN|FORMAT_HTML|FORMAT_MOODLE|FORMAT_MARKDOWN: the format of this text&lt;br /&gt;
** fullmessagehtml = &#039;long html text&#039;; html rendered version (optional)&lt;br /&gt;
** smallmessage = &#039;short text&#039;: useful for plugins like sms or twitter (optional)&lt;br /&gt;
&lt;br /&gt;
===Portfolio===&lt;br /&gt;
* portfolio_send&lt;br /&gt;
** id : recordid in portfolio_tempdata table, used for itemid in file storage&lt;br /&gt;
&lt;br /&gt;
==Events wishlist==&lt;br /&gt;
&lt;br /&gt;
List of events which it would be nice to have.  Please add to this list if what you want is not shown here.&lt;br /&gt;
&lt;br /&gt;
* user_role_assign&lt;br /&gt;
* user_role_unassign&lt;br /&gt;
* mform_print_form -- this for all types of form e.g. admin settings, user profile, module updating, + some sort of standard way of discriminiating between them e.g. if ($form-&amp;gt;name == &#039;user_profile&#039;) {}. This would be better triggered at the end of the form generation process so that new bits can be inserted at any point, or existing bits could be removed.&lt;br /&gt;
* module_installed&lt;br /&gt;
* module_removed&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=69103 General Developer Forum thread for discussing this proposal]. &lt;br /&gt;
* [[Messaging_2.0]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Coding guidelines|Events]]&lt;br /&gt;
[[Category:Grades]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6562</id>
		<title>Old Events API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6562"/>
		<updated>2010-05-04T11:40:11Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Handling an event */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Overview==&lt;br /&gt;
&lt;br /&gt;
The Events API is a core system in Moodle to allow communication between modules.  &lt;br /&gt;
&lt;br /&gt;
An &#039;&#039;&#039;event&#039;&#039;&#039; is when something &amp;quot;interesting&amp;quot; happens in Moodle that is worth alerting the system about.&lt;br /&gt;
&lt;br /&gt;
Any Moodle modules can &#039;&#039;&#039;trigger&#039;&#039;&#039; new events (with attached data), and other modules can elect to &#039;&#039;&#039;handle&#039;&#039;&#039; those events with custom functions that operate on the given data.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
Let&#039;s look at an example of how events are used to implement Messaging in Moodle 2.0.  In the messaging system, textual messages are generated for users by different modules, and the user can decide how certain types of messages are displayed.&lt;br /&gt;
&lt;br /&gt;
===Triggering an event===&lt;br /&gt;
&lt;br /&gt;
When a messaging event occurs, the module should trigger a &amp;quot;message_send&amp;quot; event.   In this example let&#039;s pretend someone just posted to a forum.&lt;br /&gt;
&lt;br /&gt;
The forum module needs to create an object with the data that this event needs.  This may vary completely for different types of events, it&#039;s just a data object.&lt;br /&gt;
&lt;br /&gt;
 $eventdata = new object();&lt;br /&gt;
 $eventdata-&amp;gt;component         = &#039;mod/forum&#039;;    // path in Moodle&lt;br /&gt;
 $eventdata-&amp;gt;name              = &#039;posts&#039;;        // type of message from that module (as module defines it)&lt;br /&gt;
 $eventdata-&amp;gt;userfrom          = $userfrom;      // user object&lt;br /&gt;
 $eventdata-&amp;gt;userto            = $userto;        // user object&lt;br /&gt;
 $eventdata-&amp;gt;subject           = &amp;quot;Hi there&amp;quot;;     // short one-line subject&lt;br /&gt;
 $eventdata-&amp;gt;fullmessage       = &amp;quot;Here is the full message&amp;quot;;      // raw text&lt;br /&gt;
 $eventdata-&amp;gt;fullmessageformat = FORMAT_PLAIN;   // text format&lt;br /&gt;
 $eventdata-&amp;gt;fullmessagehtml   = &amp;quot;Here is the &amp;amp;lt;b&amp;gt;full&amp;amp;lt;/b&amp;gt; message&amp;quot;;    // html rendered version   (optional)&lt;br /&gt;
 $eventdata-&amp;gt;smallmessage      = &amp;quot;Here is the truncated message&amp;quot;;      // useful for plugins like sms or twitter  (optional)&lt;br /&gt;
&lt;br /&gt;
Then we post the object as an event and forget about it:&lt;br /&gt;
&lt;br /&gt;
 events_trigger(&#039;message_send&#039;, $eventdata);&lt;br /&gt;
&lt;br /&gt;
===Handling an event===&lt;br /&gt;
&lt;br /&gt;
Modules or core code can define an events.php in the db directory which defines events they want to be notified about, and describes which of their functions or class methods should be notified.  For this case, there is this definition of a handler in lib/db/events.php&lt;br /&gt;
&lt;br /&gt;
 $handlers = array (&lt;br /&gt;
     &#039;message_send&#039; =&amp;gt; array (&lt;br /&gt;
          &#039;handlerfile&#039;      =&amp;gt; &#039;/lib/messagelib.php&#039;,&lt;br /&gt;
          &#039;handlerfunction&#039;  =&amp;gt; &#039;message_send_handler&#039;,&lt;br /&gt;
          &#039;schedule&#039;         =&amp;gt; &#039;instant&#039;&lt;br /&gt;
      )&lt;br /&gt;
 );&lt;br /&gt;
&lt;br /&gt;
These events.php files are parsed during install / upgrade and stored in a simple database table.&lt;br /&gt;
&lt;br /&gt;
Now, when a &#039;&#039;&#039;message_send&#039;&#039;&#039; event happens, all the registered handlers functions for that event will be called something like this (but with more error handling):&lt;br /&gt;
&lt;br /&gt;
          include_once($CFG-&amp;gt;dirroot.$handlers[&#039;message_send&#039;][&#039;handlerfile&#039;]);&lt;br /&gt;
          call_user_func($handlers[&#039;message_send&#039;][&#039;handlerfunction&#039;], $eventdata);&lt;br /&gt;
&lt;br /&gt;
Any code can hook into any events this way.&lt;br /&gt;
&lt;br /&gt;
The handler function accepts one parameter (the event data object) and should return a boolean.  Returning false indicates that there was an error and the event will be left in the event queue.&lt;br /&gt;
&lt;br /&gt;
{code}&lt;br /&gt;
function message_send_handler($eventdata) {&lt;br /&gt;
    // handle event&lt;br /&gt;
    return true;&lt;br /&gt;
}&lt;br /&gt;
{code}&lt;br /&gt;
&lt;br /&gt;
==Database structure==&lt;br /&gt;
&lt;br /&gt;
There are 3 core tables for events. Note that if a handler is queued, and yet to be processed or processing failed, then all subsequent calls on that handler must be queued.&lt;br /&gt;
&lt;br /&gt;
===events_handlers===&lt;br /&gt;
&lt;br /&gt;
This table is for storing which components requests what type of event, and the location of the responsible handler functions.&lt;br /&gt;
&lt;br /&gt;
These entries are created by parsing events.php files in all the modules, and can be rebuilt any time (during an upgrade, say).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventname&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of the event, e.g. &#039;message_send&#039;&lt;br /&gt;
|-&lt;br /&gt;
|handlermodule&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|e.g. moodle, mod/forum, block/rss_client&lt;br /&gt;
|-&lt;br /&gt;
|handlerfile&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|path to the file of the function, eg /lib/messagelib.php&lt;br /&gt;
|-&lt;br /&gt;
|handlerfunction&lt;br /&gt;
|text&lt;br /&gt;
|serialized string or array describing function, suitable to be passed to &#039;&#039;&#039;call_user_func()&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|schedule 	&lt;br /&gt;
|varchar(255) 	&lt;br /&gt;
|&#039;cron&#039; or &#039;instant&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue===&lt;br /&gt;
&lt;br /&gt;
This table is for storing queued events. It stores only one copy of the eventdata here, and entries from this table are being references by the events_queue_handlers table.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventdata 	&lt;br /&gt;
|longtext 	&lt;br /&gt;
|serialized version of the data object passed to the event handler.&lt;br /&gt;
|-&lt;br /&gt;
|stackdump&lt;br /&gt;
|text&lt;br /&gt;
|serialized debug_backtrace showing where the event was fired from&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|int(10)&lt;br /&gt;
|$USER-&amp;gt;id when the event was fired&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) 	&lt;br /&gt;
|time stamp of the first time this was added&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue_handlers===&lt;br /&gt;
&lt;br /&gt;
This is the list of queued handlers for processing. The event object is retrieved from the events_queue table. When no further reference is made to the events_queue table, the corresponding entry in the events_queue table should be deleted. Entry should get deleted (?) after a successful event processing by the specified handler.  The status field keeps track of failures, after it gets to a certain number (eg 10?) it should trigger an &amp;quot;event failed&amp;quot; event (that could result in admin being emailed etc, or perhaps even the originating module taking care of it or rolling something back etc).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|queuedeventid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_queues table&lt;br /&gt;
|-&lt;br /&gt;
|handlerid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_handlers table&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|-&lt;br /&gt;
|errormessage&lt;br /&gt;
|text&lt;br /&gt;
|if an error happened last time we tried to process this event, record it here.&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|time stamp of the last attempt to run this from the queue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Standards for naming events==&lt;br /&gt;
&lt;br /&gt;
All event names should follow a consistent naming pattern, such as modulename_noun_verb&lt;br /&gt;
&lt;br /&gt;
If the event is being fired after the action has taken place (as in most cases) then use the past tense for the verb (created / deleted / updated / sent).&lt;br /&gt;
&lt;br /&gt;
If the event &#039;&#039;&#039;is&#039;&#039;&#039; the action, then use the present tense (create / delete / update / send).&lt;br /&gt;
&lt;br /&gt;
==Events which exist==&lt;br /&gt;
&lt;br /&gt;
When we add new events to core we should always add them here too.&lt;br /&gt;
&lt;br /&gt;
Under each event, list the data sent as part of the event.&lt;br /&gt;
&lt;br /&gt;
===Users===&lt;br /&gt;
* user_created&lt;br /&gt;
** full new record from &#039;user&#039; table&lt;br /&gt;
* user_updated&lt;br /&gt;
** full new record from &#039;user&#039; table&lt;br /&gt;
&lt;br /&gt;
===Courses===&lt;br /&gt;
* course_created&lt;br /&gt;
** full course record&lt;br /&gt;
* course_updated&lt;br /&gt;
** full course record&lt;br /&gt;
* course_deleted&lt;br /&gt;
** full course record&lt;br /&gt;
* course_category_deleted&lt;br /&gt;
** full category record&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
* groups_member_added&lt;br /&gt;
** groupid&lt;br /&gt;
** userid&lt;br /&gt;
* groups_member_removed&lt;br /&gt;
** groupid&lt;br /&gt;
** userid&lt;br /&gt;
* groups_group_created&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** description&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
** picture&lt;br /&gt;
* groups_group_updated&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** description&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
** picture&lt;br /&gt;
* groups_group_deleted&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** description&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
** picture&lt;br /&gt;
* groups_grouping_created&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
* groups_grouping_updated&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
* groups_grouping_deleted&lt;br /&gt;
** id&lt;br /&gt;
** courseid&lt;br /&gt;
** name&lt;br /&gt;
** timecreated&lt;br /&gt;
** timemodified&lt;br /&gt;
* groups_members_removed (user deleted from all groups in a course)&lt;br /&gt;
** courseid&lt;br /&gt;
** userid&lt;br /&gt;
* groups_groupings_groups_removed (remove all groups from all groupings in a course)&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
* groups_groups_deleted (delete all groups in a course)&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
* groups_groupings_deleted (delete all groupings in a course)&lt;br /&gt;
** courseid (as plain integer, not object)&lt;br /&gt;
&lt;br /&gt;
===Messaging===&lt;br /&gt;
* message_send&lt;br /&gt;
** component = &#039;mod/forum&#039;: path in Moodle&lt;br /&gt;
** name = &#039;posts&#039;: type of message from that module (as module defines it)&lt;br /&gt;
** userfrom = $userfrom: a user object to send from&lt;br /&gt;
** userto = $userto: a user object to send to&lt;br /&gt;
** subject = &#039;subject line&#039;: a short text line&lt;br /&gt;
** fullmessage = &#039;full plain text&#039;: raw text as entered by user&lt;br /&gt;
** fullmessageformat = FORMAT_PLAIN|FORMAT_HTML|FORMAT_MOODLE|FORMAT_MARKDOWN: the format of this text&lt;br /&gt;
** fullmessagehtml = &#039;long html text&#039;; html rendered version (optional)&lt;br /&gt;
** smallmessage = &#039;short text&#039;: useful for plugins like sms or twitter (optional)&lt;br /&gt;
&lt;br /&gt;
===Portfolio===&lt;br /&gt;
* portfolio_send&lt;br /&gt;
** id : recordid in portfolio_tempdata table, used for itemid in file storage&lt;br /&gt;
&lt;br /&gt;
==Events wishlist==&lt;br /&gt;
&lt;br /&gt;
List of events which it would be nice to have.  Please add to this list if what you want is not shown here.&lt;br /&gt;
&lt;br /&gt;
* user_role_assign&lt;br /&gt;
* user_role_unassign&lt;br /&gt;
* mform_print_form -- this for all types of form e.g. admin settings, user profile, module updating, + some sort of standard way of discriminiating between them e.g. if ($form-&amp;gt;name == &#039;user_profile&#039;) {}. This would be better triggered at the end of the form generation process so that new bits can be inserted at any point, or existing bits could be removed.&lt;br /&gt;
* module_installed&lt;br /&gt;
* module_removed&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=69103 General Developer Forum thread for discussing this proposal]. &lt;br /&gt;
* [[Messaging_2.0]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Coding guidelines|Events]]&lt;br /&gt;
[[Category:Grades]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Talk:File_API_internals&amp;diff=27532</id>
		<title>Talk:File API internals</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Talk:File_API_internals&amp;diff=27532"/>
		<updated>2009-01-20T15:37:56Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* meta information */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Main tasks===&lt;br /&gt;
* File Storage API:&lt;br /&gt;
** abstract (M1)&lt;br /&gt;
** local pool implementation (M1)&lt;br /&gt;
** DB schema (M1)&lt;br /&gt;
** deletion, acls, metadata (M1)&lt;br /&gt;
** problem: empty directories, file overwriting&lt;br /&gt;
* File Manager API: &lt;br /&gt;
** unique class, able to handle one &amp;quot;file area&amp;quot; (M2)&lt;br /&gt;
** security (M2)&lt;br /&gt;
** hack old file manager to be able to work with new fileareas (M3)&lt;br /&gt;
** js and non js implementations of FileManager (M4)&lt;br /&gt;
** integration with editor (M4)&lt;br /&gt;
** integration with formslib (M4)&lt;br /&gt;
** integration with repos (M4)&lt;br /&gt;
** problem: zip support&lt;br /&gt;
* File Serving:&lt;br /&gt;
** from pool:&lt;br /&gt;
*** file.php&lt;br /&gt;
*** pluginfile.php&lt;br /&gt;
*** draftfile.php&lt;br /&gt;
*** userfile.php&lt;br /&gt;
** from other moddata places:&lt;br /&gt;
*** rssfile.php&lt;br /&gt;
*** user/pix.php&lt;br /&gt;
*** user/pixgroup.php&lt;br /&gt;
* Migration:&lt;br /&gt;
** course files (as much as possible, allow fallback) (M2)&lt;br /&gt;
** moddata&lt;br /&gt;
** &lt;br /&gt;
* Backup &amp;amp; restore:&lt;br /&gt;
&lt;br /&gt;
===Milestones===&lt;br /&gt;
M1: File storage API completed (this week)&lt;br /&gt;
M2: migration of course files + new filephp + FileManager + hacked old file manager (next monday)&lt;br /&gt;
M3: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===rssfile.php===&lt;br /&gt;
It&#039;s said that rss/file.php is kept only for backwards compatibility. But what exactly is meant by &amp;quot;backwards compatibility&amp;quot;? &lt;br /&gt;
Just display feeds with nice error messages and info how to re-subscribe?&lt;br /&gt;
&lt;br /&gt;
== meta information ==&lt;br /&gt;
&lt;br /&gt;
It seems like it would be nice to include some meta information about certain file types, e.g.:&lt;br /&gt;
&lt;br /&gt;
* images: width, height, alt&lt;br /&gt;
* flash files: width, height&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Talk:File_API_internals&amp;diff=27531</id>
		<title>Talk:File API internals</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Talk:File_API_internals&amp;diff=27531"/>
		<updated>2009-01-20T15:37:17Z</updated>

		<summary type="html">&lt;p&gt;Brianking: New section: meta information&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;===Main tasks===&lt;br /&gt;
* File Storage API:&lt;br /&gt;
** abstract (M1)&lt;br /&gt;
** local pool implementation (M1)&lt;br /&gt;
** DB schema (M1)&lt;br /&gt;
** deletion, acls, metadata (M1)&lt;br /&gt;
** problem: empty directories, file overwriting&lt;br /&gt;
* File Manager API: &lt;br /&gt;
** unique class, able to handle one &amp;quot;file area&amp;quot; (M2)&lt;br /&gt;
** security (M2)&lt;br /&gt;
** hack old file manager to be able to work with new fileareas (M3)&lt;br /&gt;
** js and non js implementations of FileManager (M4)&lt;br /&gt;
** integration with editor (M4)&lt;br /&gt;
** integration with formslib (M4)&lt;br /&gt;
** integration with repos (M4)&lt;br /&gt;
** problem: zip support&lt;br /&gt;
* File Serving:&lt;br /&gt;
** from pool:&lt;br /&gt;
*** file.php&lt;br /&gt;
*** pluginfile.php&lt;br /&gt;
*** draftfile.php&lt;br /&gt;
*** userfile.php&lt;br /&gt;
** from other moddata places:&lt;br /&gt;
*** rssfile.php&lt;br /&gt;
*** user/pix.php&lt;br /&gt;
*** user/pixgroup.php&lt;br /&gt;
* Migration:&lt;br /&gt;
** course files (as much as possible, allow fallback) (M2)&lt;br /&gt;
** moddata&lt;br /&gt;
** &lt;br /&gt;
* Backup &amp;amp; restore:&lt;br /&gt;
&lt;br /&gt;
===Milestones===&lt;br /&gt;
M1: File storage API completed (this week)&lt;br /&gt;
M2: migration of course files + new filephp + FileManager + hacked old file manager (next monday)&lt;br /&gt;
M3: ...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===rssfile.php===&lt;br /&gt;
It&#039;s said that rss/file.php is kept only for backwards compatibility. But what exactly is meant by &amp;quot;backwards compatibility&amp;quot;? &lt;br /&gt;
Just display feeds with nice error messages and info how to re-subscribe?&lt;br /&gt;
&lt;br /&gt;
== meta information ==&lt;br /&gt;
&lt;br /&gt;
It seems like it would be nice to include some meta information about certain file types, e.g.:&lt;br /&gt;
images: width, height, alt&lt;br /&gt;
flash files: width, height&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6544</id>
		<title>Old Events API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6544"/>
		<updated>2009-01-19T14:49:54Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Courses */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{obsolete_design}}&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
The Events API is a new core system in Moodle to allow better communication between modules.  It&#039;s based on modules triggering new events with attached data, and the other modules handling those events with custom functions.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
We&#039;ll be using the example of a grade being posted from a module into the [[Grades|new gradebook in Moodle 1.9]], but there are obviously all kinds of events possible.&lt;br /&gt;
&lt;br /&gt;
===Triggering an event===&lt;br /&gt;
&lt;br /&gt;
Whenever a grade is created or changed by a module, it should “tell” the system about it (in addition to any local working storage it uses).   So, using the quiz as an example, we first define an object as follows:&lt;br /&gt;
&lt;br /&gt;
 $eventdata = new object();&lt;br /&gt;
 $eventdata-&amp;gt;itemid = $grade_item-&amp;gt;id;&lt;br /&gt;
 $eventdata-&amp;gt;userid = $USER-&amp;gt;id;&lt;br /&gt;
 $eventdata-&amp;gt;gradevalue = $currentvalue;&lt;br /&gt;
&lt;br /&gt;
Then we post the object as an event and forget about it:&lt;br /&gt;
&lt;br /&gt;
 events_trigger(&#039;grade_updated&#039;, $eventdata);&lt;br /&gt;
&lt;br /&gt;
===Handling an event===&lt;br /&gt;
&lt;br /&gt;
Modules can define an events.php in their db directory which defines events they want to be notified about, and describes which of their functions or class methods should be notified.   For example, an export  plugin could register something like:&lt;br /&gt;
&lt;br /&gt;
 $handlers = array (&lt;br /&gt;
     &#039;grade_updated&#039; =&amp;gt; array (&lt;br /&gt;
         &#039;handlerfile&#039;      =&amp;gt; &#039;/grade/export/banner/lib.php&#039;,&lt;br /&gt;
         &#039;handlerfunction&#039;  =&amp;gt; &#039;banner_handle_grade_test&#039;,    // argument to call_user_func(), could be an array&lt;br /&gt;
         &#039;schedule&#039;         =&amp;gt; &#039;cron&#039;&lt;br /&gt;
     ) &lt;br /&gt;
 );&lt;br /&gt;
These are parsed during install / upgrade and stored in a simple database table.&lt;br /&gt;
&lt;br /&gt;
Then, when a grade_updated event happens, all the registered functions for that event will be called something like this (but with more error handling):&lt;br /&gt;
&lt;br /&gt;
          include_once($CFG-&amp;gt;dirroot.$handlers[&#039;grade_updated&#039;][&#039;handlerfile&#039;]);&lt;br /&gt;
          call_user_func($handlers[&#039;grade_updated&#039;][&#039;handlerfunction&#039;], $eventdata);&lt;br /&gt;
&lt;br /&gt;
All plugins in Moodle have access to this and can this easily “hook in” to &#039;grade_updated&#039; events (and of course any other events).&lt;br /&gt;
&lt;br /&gt;
==Database structure==&lt;br /&gt;
&lt;br /&gt;
There are 3 core tables for events. Note that if a handler is queued, and yet to be processed or processing failed, then all subsequent calls on that handler must be queued.&lt;br /&gt;
&lt;br /&gt;
===events_handlers===&lt;br /&gt;
&lt;br /&gt;
This table is for storing which components requests what type of event, and the location of the responsible handlers. For example, the grade book can register &#039;grade_added&#039; event with a function add_grade() that should be called any time an &#039;grade_added&#039; event is triggered by a module.&lt;br /&gt;
&lt;br /&gt;
These entries are created by parsing events.php files in all the modules, and can be rebuilt any time (during an upgrade, say).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventname&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of the event, e.g. &#039;grade_updated&#039;&lt;br /&gt;
|-&lt;br /&gt;
|handlermodule&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|e.g. moodle, mod/forum, block/rss_client&lt;br /&gt;
|-&lt;br /&gt;
|handlerfile&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|path to the file of the function, eg /grade/export/lib.php&lt;br /&gt;
|-&lt;br /&gt;
|handlerfunction&lt;br /&gt;
|text&lt;br /&gt;
|serialized string or array describing function, suitable to be passed to &#039;&#039;&#039;call_user_func()&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|schedule 	&lt;br /&gt;
|varchar(255) 	&lt;br /&gt;
|&#039;cron&#039; or &#039;instant&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue===&lt;br /&gt;
&lt;br /&gt;
This table is for storing queued events. It stores only one copy of the eventdata here, and entries from this table are being references by the events_queue_handlers table.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventdata 	&lt;br /&gt;
|longtext 	&lt;br /&gt;
|serialized version of the data object passed to the event handler.&lt;br /&gt;
|-&lt;br /&gt;
|stackdump&lt;br /&gt;
|text&lt;br /&gt;
|serialized debug_backtrace showing where the event was fired from&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|int(10)&lt;br /&gt;
|$USER-&amp;gt;id when the event was fired&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) 	&lt;br /&gt;
|time stamp of the first time this was added&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue_handlers===&lt;br /&gt;
&lt;br /&gt;
This is the list of queued handlers for processing. The event object is retrieved from the events_queue table. When no further reference is made to the events_queue table, the corresponding entry in the events_queue table should be deleted. Entry should get deleted (?) after a successful event processing by the specified handler.  The status field keeps track of failures, after it gets to a certain number (eg 10?) it should trigger an &amp;quot;event failed&amp;quot; event (that could result in admin being emailed etc, or perhaps even the originating module taking care of it or rolling something back etc).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|queuedeventid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_queues table&lt;br /&gt;
|-&lt;br /&gt;
|handlerid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_handlers table&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|-&lt;br /&gt;
|errormessage&lt;br /&gt;
|text&lt;br /&gt;
|if an error happened last time we tried to process this event, record it here.&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|time stamp of the last attempt to run this from the queue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Standards for naming events==&lt;br /&gt;
&lt;br /&gt;
All event names should follow a consistent naming pattern, such as modulename_noun_verb&lt;br /&gt;
&lt;br /&gt;
==Events which exist==&lt;br /&gt;
&lt;br /&gt;
===Users===&lt;br /&gt;
* user_updated&lt;br /&gt;
* password_changed&lt;br /&gt;
&lt;br /&gt;
===Courses===&lt;br /&gt;
* course_created&lt;br /&gt;
* course_updated&lt;br /&gt;
* course_deleted&lt;br /&gt;
* category_updated&lt;br /&gt;
* category_deleted&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
* group_deleted&lt;br /&gt;
* grouping_deleted&lt;br /&gt;
* group_user_added&lt;br /&gt;
* group_user_removed&lt;br /&gt;
&lt;br /&gt;
==Events wishlist==&lt;br /&gt;
&lt;br /&gt;
List of events which it would be nice to have.  Please add to this list if what you want is not shown here.&lt;br /&gt;
&lt;br /&gt;
* user_created (for example to handle custom emails.  this is commonly desired: e.g. send a custom email to a related person (teacher, boss, etc.) based on some institution-specific logic)&lt;br /&gt;
* user_enrolled_in_course&lt;br /&gt;
* user_unenrolled_from_course&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=69103 General Developer Forum thread for discussing this proposal]. &lt;br /&gt;
* [[:Development:Grades]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Events]]&lt;br /&gt;
[[Category:Grades]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6543</id>
		<title>Old Events API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6543"/>
		<updated>2009-01-19T14:49:36Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Events wishlist */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{obsolete_design}}&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
The Events API is a new core system in Moodle to allow better communication between modules.  It&#039;s based on modules triggering new events with attached data, and the other modules handling those events with custom functions.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
We&#039;ll be using the example of a grade being posted from a module into the [[Grades|new gradebook in Moodle 1.9]], but there are obviously all kinds of events possible.&lt;br /&gt;
&lt;br /&gt;
===Triggering an event===&lt;br /&gt;
&lt;br /&gt;
Whenever a grade is created or changed by a module, it should “tell” the system about it (in addition to any local working storage it uses).   So, using the quiz as an example, we first define an object as follows:&lt;br /&gt;
&lt;br /&gt;
 $eventdata = new object();&lt;br /&gt;
 $eventdata-&amp;gt;itemid = $grade_item-&amp;gt;id;&lt;br /&gt;
 $eventdata-&amp;gt;userid = $USER-&amp;gt;id;&lt;br /&gt;
 $eventdata-&amp;gt;gradevalue = $currentvalue;&lt;br /&gt;
&lt;br /&gt;
Then we post the object as an event and forget about it:&lt;br /&gt;
&lt;br /&gt;
 events_trigger(&#039;grade_updated&#039;, $eventdata);&lt;br /&gt;
&lt;br /&gt;
===Handling an event===&lt;br /&gt;
&lt;br /&gt;
Modules can define an events.php in their db directory which defines events they want to be notified about, and describes which of their functions or class methods should be notified.   For example, an export  plugin could register something like:&lt;br /&gt;
&lt;br /&gt;
 $handlers = array (&lt;br /&gt;
     &#039;grade_updated&#039; =&amp;gt; array (&lt;br /&gt;
         &#039;handlerfile&#039;      =&amp;gt; &#039;/grade/export/banner/lib.php&#039;,&lt;br /&gt;
         &#039;handlerfunction&#039;  =&amp;gt; &#039;banner_handle_grade_test&#039;,    // argument to call_user_func(), could be an array&lt;br /&gt;
         &#039;schedule&#039;         =&amp;gt; &#039;cron&#039;&lt;br /&gt;
     ) &lt;br /&gt;
 );&lt;br /&gt;
These are parsed during install / upgrade and stored in a simple database table.&lt;br /&gt;
&lt;br /&gt;
Then, when a grade_updated event happens, all the registered functions for that event will be called something like this (but with more error handling):&lt;br /&gt;
&lt;br /&gt;
          include_once($CFG-&amp;gt;dirroot.$handlers[&#039;grade_updated&#039;][&#039;handlerfile&#039;]);&lt;br /&gt;
          call_user_func($handlers[&#039;grade_updated&#039;][&#039;handlerfunction&#039;], $eventdata);&lt;br /&gt;
&lt;br /&gt;
All plugins in Moodle have access to this and can this easily “hook in” to &#039;grade_updated&#039; events (and of course any other events).&lt;br /&gt;
&lt;br /&gt;
==Database structure==&lt;br /&gt;
&lt;br /&gt;
There are 3 core tables for events. Note that if a handler is queued, and yet to be processed or processing failed, then all subsequent calls on that handler must be queued.&lt;br /&gt;
&lt;br /&gt;
===events_handlers===&lt;br /&gt;
&lt;br /&gt;
This table is for storing which components requests what type of event, and the location of the responsible handlers. For example, the grade book can register &#039;grade_added&#039; event with a function add_grade() that should be called any time an &#039;grade_added&#039; event is triggered by a module.&lt;br /&gt;
&lt;br /&gt;
These entries are created by parsing events.php files in all the modules, and can be rebuilt any time (during an upgrade, say).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventname&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of the event, e.g. &#039;grade_updated&#039;&lt;br /&gt;
|-&lt;br /&gt;
|handlermodule&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|e.g. moodle, mod/forum, block/rss_client&lt;br /&gt;
|-&lt;br /&gt;
|handlerfile&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|path to the file of the function, eg /grade/export/lib.php&lt;br /&gt;
|-&lt;br /&gt;
|handlerfunction&lt;br /&gt;
|text&lt;br /&gt;
|serialized string or array describing function, suitable to be passed to &#039;&#039;&#039;call_user_func()&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|schedule 	&lt;br /&gt;
|varchar(255) 	&lt;br /&gt;
|&#039;cron&#039; or &#039;instant&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue===&lt;br /&gt;
&lt;br /&gt;
This table is for storing queued events. It stores only one copy of the eventdata here, and entries from this table are being references by the events_queue_handlers table.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventdata 	&lt;br /&gt;
|longtext 	&lt;br /&gt;
|serialized version of the data object passed to the event handler.&lt;br /&gt;
|-&lt;br /&gt;
|stackdump&lt;br /&gt;
|text&lt;br /&gt;
|serialized debug_backtrace showing where the event was fired from&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|int(10)&lt;br /&gt;
|$USER-&amp;gt;id when the event was fired&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) 	&lt;br /&gt;
|time stamp of the first time this was added&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue_handlers===&lt;br /&gt;
&lt;br /&gt;
This is the list of queued handlers for processing. The event object is retrieved from the events_queue table. When no further reference is made to the events_queue table, the corresponding entry in the events_queue table should be deleted. Entry should get deleted (?) after a successful event processing by the specified handler.  The status field keeps track of failures, after it gets to a certain number (eg 10?) it should trigger an &amp;quot;event failed&amp;quot; event (that could result in admin being emailed etc, or perhaps even the originating module taking care of it or rolling something back etc).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|queuedeventid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_queues table&lt;br /&gt;
|-&lt;br /&gt;
|handlerid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_handlers table&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|-&lt;br /&gt;
|errormessage&lt;br /&gt;
|text&lt;br /&gt;
|if an error happened last time we tried to process this event, record it here.&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|time stamp of the last attempt to run this from the queue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Standards for naming events==&lt;br /&gt;
&lt;br /&gt;
All event names should follow a consistent naming pattern, such as modulename_noun_verb&lt;br /&gt;
&lt;br /&gt;
==Events which exist==&lt;br /&gt;
&lt;br /&gt;
===Users===&lt;br /&gt;
* user_updated&lt;br /&gt;
* password_changed&lt;br /&gt;
&lt;br /&gt;
===Courses===&lt;br /&gt;
* course_updated&lt;br /&gt;
* course_deleted&lt;br /&gt;
* category_updated&lt;br /&gt;
* category_deleted&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
* group_deleted&lt;br /&gt;
* grouping_deleted&lt;br /&gt;
* group_user_added&lt;br /&gt;
* group_user_removed&lt;br /&gt;
&lt;br /&gt;
==Events wishlist==&lt;br /&gt;
&lt;br /&gt;
List of events which it would be nice to have.  Please add to this list if what you want is not shown here.&lt;br /&gt;
&lt;br /&gt;
* user_created (for example to handle custom emails.  this is commonly desired: e.g. send a custom email to a related person (teacher, boss, etc.) based on some institution-specific logic)&lt;br /&gt;
* user_enrolled_in_course&lt;br /&gt;
* user_unenrolled_from_course&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=69103 General Developer Forum thread for discussing this proposal]. &lt;br /&gt;
* [[:Development:Grades]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Events]]&lt;br /&gt;
[[Category:Grades]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=lib/formslib.php_Form_Definition&amp;diff=5796</id>
		<title>lib/formslib.php Form Definition</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=lib/formslib.php_Form_Definition&amp;diff=5796"/>
		<updated>2008-05-02T15:37:33Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* multi-select */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Formslib}}&lt;br /&gt;
==definition()==&lt;br /&gt;
&lt;br /&gt;
The definition of the elements to be included in the form, their &#039;types&#039; (PARAM_*), helpbuttons included, etc is all included in a function you must define in your class &#039;definition();&#039;&lt;br /&gt;
&lt;br /&gt;
definition() is used to define the elements in the form and &#039;&#039;&#039;this definition will be used for validating data submitted as well as for printing the form.&#039;&#039;&#039; For select and checkbox type elements only data that could have been selected will be allowed. And only data that corresponds to a form element in the defintion will be accepted as submitted data.&lt;br /&gt;
&lt;br /&gt;
The definition() should include all elements that are going to be used on form, some elements may be removed or tweaked later in definition_after_data(). Please do not create conditional elements in definition(), the definition() should not directly depend on the submitted data.&lt;br /&gt;
&lt;br /&gt;
==Use Fieldsets to group Form Elements==&lt;br /&gt;
&lt;br /&gt;
You use code like this to open a fieldset with a legend.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//-------------------------------------------------------------------------------&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;header&#039;, &#039;nameforyourheaderelement&#039;, get_string(&#039;titleforlegened&#039;, &#039;modulename&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can&#039;t yet nest these visible fieldsets unfortunately. But in fact groups of elements are wrapped in invisible fieldsets.&lt;br /&gt;
&lt;br /&gt;
You close a fieldset with moodle_form&#039;s closeHeaderBefore method. You tell closeHeaderBefore the element before you wish to end the fieldset. A fieldset is automatically closed if you open a new one. You need to use this code only if you want to close a fieldset and the subsequent form elements are not to be enclosed by a visible fieldset (they are still enclosed with an invisibe one with no legend) :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;closeHeaderBefore(&#039;buttonar&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==addElement==&lt;br /&gt;
&lt;br /&gt;
Use the addElement method to add an element to a form. The first few arguments are always the same. The first param is the type of the element to add. The second is the elementname to use which is normally the html name of the element in the form. The third is often the text for the label for the element.&lt;br /&gt;
&lt;br /&gt;
Some examples are below :&lt;br /&gt;
===button===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            $mform-&amp;gt;addElement(&#039;button&#039;, &#039;intro&#039;, get_string(&amp;quot;buttonlabel&amp;quot;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A button element. If you want a submit or cancel button see &#039;submit&#039; element. &lt;br /&gt;
&lt;br /&gt;
===checkbox===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;checkbox&#039;, &#039;ratingtime&#039;, get_string(&#039;ratingtime&#039;, &#039;forum&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is a simple checkbox. The third param for this element is the label to display on the left side of the form. You can also supply a string as a fourth param to specify a label that will appear on the right of the element. Checkboxes and radio buttons can be grouped and have individual labels on their right.&lt;br /&gt;
&lt;br /&gt;
====advcheckbox====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;advcheckbox&#039;, &#039;ratingtime&#039;, get_string(&#039;ratingtime&#039;, &#039;forum&#039;), array(0, 1), array(&#039;group&#039; =&amp;gt; 1));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Similar to the checkbox above, but with a couple of important improvements:&lt;br /&gt;
&lt;br /&gt;
#The 4th parameter is an array of values that will be associated with the checked/unchecked state of the checkbox. With a normal checkbox you cannot choose that value, and in fact an unchecked checkbox will not even be sent with the form data.&lt;br /&gt;
#The 5th parameter is a normal $attributes array, normally used to set HTML attributes for the &amp;lt;input&amp;gt; element. However, a special value of &#039;group&#039; can be given, which will add a class name to the element, and enable its grouping for a [[lib/formslib.php_add_checkbox_controller|checkbox controller]]&lt;br /&gt;
&lt;br /&gt;
===choosecoursefile===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;choosecoursefile&#039;, &#039;mediafile&#039;, get_string(&#039;mediafile&#039;, &#039;lesson&#039;), array(&#039;courseid&#039;=&amp;gt;$COURSE-&amp;gt;id));&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Choose a file from the course files area. The fourth option is a list of options for the element. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
array(&#039;courseid&#039;=&amp;gt;null,\\if it is null (default then use globabl $COURSE&lt;br /&gt;
 &#039;height&#039;=&amp;gt;500,\\ height of the popup window&lt;br /&gt;
 &#039;width&#039;=&amp;gt;750, \\ width of the popup window&lt;br /&gt;
&#039;options&#039;=&amp;gt;&#039;none&#039;);\\options string for the pop up window &lt;br /&gt;
                   \\eg. &#039;menubar=0,location=0,scrollbars,resizable&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===date_selector===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;date_selector&#039;, &#039;assesstimefinish&#039;, get_string(&#039;to&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is a date selector. You can select a Day, Month and Year using a group of select boxes. The fourth param here is an array of options. The defaults for the options are :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;array(&#039;startyear&#039;=&amp;gt;1970, &#039;stopyear&#039;=&amp;gt;2020,&lt;br /&gt;
                    &#039;timezone&#039;=&amp;gt;99, &#039;applydst&#039;=&amp;gt;true);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can override these defaults by supplying an array as fourth param with one or more keys with a value to override the default. You can supply a fifth param of attributes here as well.&lt;br /&gt;
&lt;br /&gt;
===date_time_selector===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;date_time_selector&#039;, &#039;assesstimestart&#039;, get_string(&#039;from&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is a group of select boxes to select a date (Day Month and Year) and time (Hour and Minute). When submitted, submitted data is processed and a timestamp is passed to $form-&amp;gt;get_data(); the fourth param here is an array of options. The defaults for the options are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;array(&#039;startyear&#039;=&amp;gt;1970, &#039;stopyear&#039;=&amp;gt;2020,&lt;br /&gt;
                    &#039;timezone&#039;=&amp;gt;99, &#039;applydst&#039;=&amp;gt;true, &#039;step&#039;=&amp;gt;5);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can override these defaults by supplying an array as fourth param with one or more keys with a value to override the default. You can supply a fifth param of attributes here as well.&lt;br /&gt;
&lt;br /&gt;
===file===&lt;br /&gt;
&lt;br /&gt;
File upload input box with browse button. In the form definition type&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            $mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
after form submission and validation use&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
              ...&lt;br /&gt;
              $mform-&amp;gt;save_files($destination_directory);&lt;br /&gt;
              ...&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need advanced settings such as required file, different max upload size or name of uploaded file&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            $this-&amp;gt;set_upload_manager(new upload_manager(&#039;attachment&#039;, true, false, $COURSE, false, 0, true, true, false));&lt;br /&gt;
            $mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
            $mform-&amp;gt;addRule(&#039;attachment&#039;, null, &#039;required&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
              ...&lt;br /&gt;
              $mform-&amp;gt;save_files($destination_directory);&lt;br /&gt;
              $newfilename = $mform-&amp;gt;get_new_filename();&lt;br /&gt;
              ...&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When porting old code it is also possible to use the upload manager directly for processing of uploaded files.&lt;br /&gt;
&lt;br /&gt;
Please note that if using set_upload_manager() it must be before addElement(&#039;file&#039;,..).&lt;br /&gt;
&lt;br /&gt;
===hidden===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;reply&#039;, &#039;yes&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A hidden element. Set the element name (in this case &#039;&#039;&#039;reply&#039;&#039;&#039;) to the stated value (in this case &#039;&#039;&#039;yes&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
===htmleditor &amp;amp; format===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;htmleditor&#039;, &#039;text&#039;, get_string(&#039;choicetext&#039;, &#039;choice&#039;));&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;text&#039;, PARAM_RAW);&lt;br /&gt;
	$mform-&amp;gt;addRule(&#039;text&#039;, null, &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;format&#039;, &#039;format&#039;, get_string(&#039;format&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can supply a fourth param to htmleditor of an array of options :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
array(&#039;canUseHtmlEditor&#039;=&amp;gt;&#039;detect&#039;,&#039;rows&#039;=&amp;gt;10, &#039;cols&#039;=&amp;gt;65, &lt;br /&gt;
&#039;width&#039;=&amp;gt;0,&#039;height&#039;=&amp;gt;0, &#039;course&#039;=&amp;gt;0);&lt;br /&gt;
//options same as print_textarea params&lt;br /&gt;
//use rows and cols options to control htmleditor size.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===modgrade===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;modgrade&#039;, &#039;scale&#039;, get_string(&#039;grade&#039;), false);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is a custom element for selecting a grade for any activity module. The fourth argument is whether to include an option for no grade which has a value 0. This select box does include scales. The default is true, include no grade option.&lt;br /&gt;
&lt;br /&gt;
A helpbutton is automatically added.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===password===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         $mform-&amp;gt;addElement(&#039;password&#039;, &#039;password&#039;, get_string(&#039;label&#039;), $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A password element. Fourth param is an array or string of attributes.&lt;br /&gt;
&lt;br /&gt;
===passwordunmask===&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         $mform-&amp;gt;addElement(&#039;passwordunmask&#039;, &#039;password&#039;, get_string(&#039;label&#039;), $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A password element with option to show the password in plaintext. Fourth param is an array or string of attributes.&lt;br /&gt;
&lt;br /&gt;
===radio===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $radioarray=array();&lt;br /&gt;
        $radioarray[] = &amp;amp;MoodleQuickForm::createElement(&#039;radio&#039;, &#039;yesno&#039;, &#039;&#039;, get_string(&#039;yes&#039;), 1, $attributes);&lt;br /&gt;
        $radioarray[] = &amp;amp;MoodleQuickForm::createElement(&#039;radio&#039;, &#039;yesno&#039;, &#039;&#039;, get_string(&#039;no&#039;), 0, $attributes);&lt;br /&gt;
        $mform-&amp;gt;addGroup($radioarray, &#039;radioar&#039;, &#039;&#039;, array(&#039; &#039;), false);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Second param names the radio button and should be the same for each button in the group in order to toggle correctly. Third param would be the label for the form element but is generally ignored as this element will always be in a group which has it&#039;s own label. Fourth param is a string, a label to be displayed on the right of the element. The fifth is the value for this radio button. $attributes can be a string or an array of attributes.&lt;br /&gt;
&lt;br /&gt;
====setDefault====&lt;br /&gt;
&lt;br /&gt;
To set the default for a radio button group as above use the following :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;setDefault(&#039;yesno&#039;, 0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would make the default &#039;no&#039;.&lt;br /&gt;
&lt;br /&gt;
===select===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;select&#039;, &#039;type&#039;, get_string(&#039;forumtype&#039;, &#039;forum&#039;), $FORUM_TYPES, $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The fourth param for this element is an array of options for the select box. The keys are the values for the option and the value of the array is the text for the option. The fifth param $attributes is optional, see text element for description of attributes param.&lt;br /&gt;
&lt;br /&gt;
====multi-select====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $select = &amp;amp;$mform-&amp;gt;addElement(&#039;select&#039;, &#039;colors&#039;, get_string(&#039;colors&#039;), array(&#039;red&#039;, &#039;blue&#039;, &#039;green&#039;), $attributes);&lt;br /&gt;
        $select-&amp;gt;setMultiple(true);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===selectyesno===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;maxbytes&#039;, get_string(&#039;maxattachmentsize&#039;, &#039;forum&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want a yes / no select box this one automatically translates itself and has value 1 for yes and 0 for no.&lt;br /&gt;
&lt;br /&gt;
===static===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         $mform-&amp;gt;addElement(&#039;static&#039;, &#039;description&#039;, get_string(&#039;description&#039;, &#039;exercise&#039;),&lt;br /&gt;
                  get_string(&#039;descriptionofexercise&#039;, &#039;exercise&#039;, $COURSE-&amp;gt;students));&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is a static element. It should be used with care it is used to display a static piece of text with a label. The third param is the label and the fourth is the static text itself.&lt;br /&gt;
&lt;br /&gt;
===submit, reset and cancel===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        //normally you use add_action_buttons instead of this code&lt;br /&gt;
        $buttonarray=array();&lt;br /&gt;
        $buttonarray[] = &amp;amp;$mform-&amp;gt;createElement(&#039;submit&#039;, &#039;submitbutton&#039;, get_string(&#039;savechanges&#039;));&lt;br /&gt;
        $buttonarray[] = &amp;amp;$mform-&amp;gt;createElement(&#039;reset&#039;, &#039;resetbutton&#039;, get_string(&#039;revert&#039;));&lt;br /&gt;
        $buttonarray[] = &amp;amp;$mform-&amp;gt;createElement(&#039;cancel&#039;);&lt;br /&gt;
        $mform-&amp;gt;addGroup($buttonarray, &#039;buttonar&#039;, &#039;&#039;, array(&#039; &#039;), false);&lt;br /&gt;
        $mform-&amp;gt;closeHeaderBefore(&#039;buttonar&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A &#039;Submit&#039; type element is a submit type form element which will submit the form. A &#039;Reset&#039; will not submit the form but will reset any changes the user has made to form contents. A &#039;Cancel&#039; element cancels form submission. You need to have a branch in your code before you check for get_data() to check if submission has been cancelled with is_cancelled(); See the example on the usage page.&lt;br /&gt;
&lt;br /&gt;
You should name your submit and reset buttons &#039;submitbutton&#039; and &#039;resetbutton&#039; or something similar (not &#039;submit&#039; and &#039;reset&#039;). This avoids problems in JavaScript of collisions between form element names and names of JavaScript methods of the form object.&lt;br /&gt;
&lt;br /&gt;
====add_action_buttons($cancel = true, $submitlabel=null);====&lt;br /&gt;
&lt;br /&gt;
You will normally use this helper function which is a method of moodleform to add all the &#039;action&#039; buttons to the end of your form. A boolean parameter allow you to specify whether to include a cancel button and specify the label for your submit button (pass the result of get_string). Default for the submit button label is get_string(&#039;savechanges&#039;).&lt;br /&gt;
&lt;br /&gt;
===text===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;name&#039;, get_string(&#039;forumname&#039;, &#039;forum&#039;), $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For a simple text element. Your fourth parameter here can be a string or array of attributes for the text element. The following are equivalent :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $attributes=&#039;size=&amp;quot;20&amp;quot;&#039;;&lt;br /&gt;
        $attributes=array(&#039;size&#039;=&amp;gt;&#039;20&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Generally you are encouraged to use CSS instead of using attributes for styling.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A format element can be used as a format select box. It will be non-selectable if you&#039;re using an html editor.&lt;br /&gt;
&lt;br /&gt;
The third param for this element is $useHtmlEditor and it defaults to null in which case an html editor is used if the browser and user profile support it.&lt;br /&gt;
&lt;br /&gt;
===textarea===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            $mform-&amp;gt;addElement(&#039;textarea&#039;, &#039;introduction&#039;, get_string(&amp;quot;introtext&amp;quot;, &amp;quot;survey&amp;quot;), &#039;wrap=&amp;quot;virtual&amp;quot; rows=&amp;quot;20&amp;quot; cols=&amp;quot;50&amp;quot;&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A textarea element. If you want an htmleditor use htmleditor element. Fourth element here is a string or array of attributes.&lt;br /&gt;
&lt;br /&gt;
==addGroup==&lt;br /&gt;
&lt;br /&gt;
A &#039;group&#039; in formslib is just a group of elements that will have a label and will be included on one line. &lt;br /&gt;
&lt;br /&gt;
For example typical code to include a submit and cancel button on the same line : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $buttonarray=array();&lt;br /&gt;
        $buttonarray[] =&amp;amp; $mform-&amp;gt;createElement(&#039;submit&#039;, &#039;submitbutton&#039;, get_string(&#039;savechanges&#039;));&lt;br /&gt;
        $buttonarray[] =&amp;amp; $mform-&amp;gt;createElement(&#039;submit&#039;, &#039;cancel&#039;, get_string(&#039;cancel&#039;));&lt;br /&gt;
        $mform-&amp;gt;addGroup($buttonarray, &#039;buttonar&#039;, &#039;&#039;, array(&#039; &#039;), false);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You use the same arguments for createElement as you do for addElement. Any label for the element in the third argument is normally ignored (but not in the case of the submit buttons above where the third argument is not for a label but is the text for the button).&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of putting a date_selector (which is itself a group of elements) and a checkbox on the same line, note that you can disable every element in the group using the group name &#039;availablefromgroup&#039; but it doesn&#039;t disable the controlling element the &#039;availablefromenabled&#039; checkbox:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $availablefromgroup=array();&lt;br /&gt;
	$availablefromgroup[] =&amp;amp; $mform-&amp;gt;createElement(&#039;date_selector&#039;, &#039;availablefrom&#039;, &#039;&#039;);&lt;br /&gt;
	$availablefromgroup[] =&amp;amp; $mform-&amp;gt;createElement(&#039;checkbox&#039;, &#039;availablefromenabled&#039;, &#039;&#039;, get_string(&#039;enable&#039;));&lt;br /&gt;
        $mform-&amp;gt;addGroup($availablefromgroup, &#039;availablefromgroup&#039;, get_string(&#039;availablefromdate&#039;, &#039;data&#039;), &#039;&amp;amp;nbsp;&#039;, false);&lt;br /&gt;
        $mform-&amp;gt;disabledIf(&#039;availablefromgroup&#039;, &#039;availablefromenabled&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==setHelpButton==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            $mform-&amp;gt;setHelpButton(&#039;lessondefault&#039;, array(&#039;lessondefault&#039;, get_string(&#039;lessondefault&#039;, &#039;lesson&#039;), &#039;lesson&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
First param is an elementname and the second param is an array of params that are passed to helpbutton in weblib.php. Params are :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 * @param string $page  The keyword that defines a help page&lt;br /&gt;
 * @param string $title The title of links, rollover tips, alt tags etc&lt;br /&gt;
 *           &#039;Help with&#039; (or the language equivalent) will be prefixed and &#039;...&#039; will be stripped.&lt;br /&gt;
 * @param string $module Which module is the page defined in&lt;br /&gt;
 * @param mixed $image Use a help image for the link?  (true/false/&amp;quot;both&amp;quot;)&lt;br /&gt;
 * @param boolean $linktext If true, display the title next to the help icon.&lt;br /&gt;
 * @param string $text If defined then this text is used in the page, and&lt;br /&gt;
 *           the $page variable is ignored.&lt;br /&gt;
 * @param boolean $return If true then the output is returned as a string, if false it is printed to the current page.&lt;br /&gt;
 * @param string $imagetext The full text for the helpbutton icon. If empty use default help.gif&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Make sure you don&#039;t set boolean $return to false. &lt;br /&gt;
&lt;br /&gt;
You need to do use this method after addElement();&lt;br /&gt;
&lt;br /&gt;
==setDefault==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;select&#039;, &#039;grade&#039;, get_string(&#039;gradeforsubmission&#039;, &#039;exercise&#039;), $grades);&lt;br /&gt;
        $mform-&amp;gt;setHelpButton(&#039;grade&#039;, array(&#039;grade&#039;, get_string(&#039;gradeforsubmission&#039;, &#039;exercise&#039;), &#039;exercise&#039;));&lt;br /&gt;
        $mform-&amp;gt;setDefault(&#039;grade&#039;, 100);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set the default of the form value with setDefault($elementname, $value); where elementname is the elementname whose default you want to set and $value is the default to set. We set the defaults for the form in definition(). This default is what is used if no data is loaded into the form with set_data(); eg. on display of the form for an &#039;add&#039; rather than &#039;update&#039; function.&lt;br /&gt;
&lt;br /&gt;
==disabledIf==&lt;br /&gt;
&lt;br /&gt;
For any element or groups of element in a form you can conditionally disable the group or individual element depending on conditions.&lt;br /&gt;
&lt;br /&gt;
You can use $mform-&amp;gt;disabledIf($elementName, $dependentOn, $condition = &#039;notchecked&#039;, $value=null)&lt;br /&gt;
&lt;br /&gt;
* elementname can be a group. If you specify a group all elements in the group will be disabled (if dependentOn is in elementname group that is ignored and not disabled). These are the element names you&#039;ve used as the first argument in addElement or addGroup.&lt;br /&gt;
* dependentOn is the actual name of the element as it will appear in html. This can be different to the name used in addGroup particularly but also addElement where you&#039;re adding a complex element like a date_selector. Check the html of your page. You typically make the depedentOn a checkbox or select box.&lt;br /&gt;
* $condition will be notchecked, checked, selected, eq or if it is anything else then we test for neq.&lt;br /&gt;
* If $condition is eq or neq then we check the value of the dependentOn field and check for equality (==) or nonequality (!=) in js&lt;br /&gt;
* If $condition is checked or notchecked then we check to see if a checkbox is checked or not.&lt;br /&gt;
* If $condition is selected then we check to see if a particular option is selected from a dropdown list.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: I am not sure this section is complete. I just found and added one missing case, but there may be others--[[User:Tim Hunt|Tim Hunt]] 06:04, 15 October 2007 (CDT)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==setType==&lt;br /&gt;
&lt;br /&gt;
PARAM_* types are used to specify how a submitted variable should be cleaned. These should be used for get parameters such as id, course etc. which are used to load a page and also with setType(); method. Every form element should have a type specified except select, radio box and checkbox elements, these elements do a good job of cleaning themselves (only specified options are allowed as user input).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Most Commonly Used PARAM_* Types===&lt;br /&gt;
&lt;br /&gt;
These are the most commonly used PARAM_* types and their proper uses. More types can be seen in moodlelib.php starting around line 100.&lt;br /&gt;
&lt;br /&gt;
* PARAM_CLEAN is deprecated and you should try to use a more specific type.&lt;br /&gt;
* PARAM_TEXT should be used for cleaning data that is expected to be plain text. It will strip all html tags. But will still let tags for multilang support through.&lt;br /&gt;
* PARAM_NOTAGS should be used for cleaning data that is expected to be plain text. It will strip *all* html type tags. It will still *not* let tags for multilang support through. This should be used for instance for email addresses where no multilang support is appropriate.&lt;br /&gt;
* PARAM_RAW means no cleaning whatsoever, it is used mostly for data from the html editor. Data from the editor is later cleaned before display using format_text() function. PARAM_RAW can also be used for data that is validated by some other way or printed by p() or s().&lt;br /&gt;
* PARAM_INT should be used for integers.&lt;br /&gt;
* PARAM_ACTION is an alias of PARAM_ALPHA and is used for hidden fields specifying form actions.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
* [http://midnighthax.com/quickform.php PEAR HTML QuickForm Getting Started Guide by Keith Edmunds of Midnighthax.com]&lt;br /&gt;
&lt;br /&gt;
[[Category:Formslib]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=lib/formslib.php_Form_Definition&amp;diff=5795</id>
		<title>lib/formslib.php Form Definition</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=lib/formslib.php_Form_Definition&amp;diff=5795"/>
		<updated>2008-04-16T21:30:54Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* select */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Formslib}}&lt;br /&gt;
==definition()==&lt;br /&gt;
&lt;br /&gt;
The definition of the elements to be included in the form, their &#039;types&#039; (PARAM_*), helpbuttons included, etc is all included in a function you must define in your class &#039;definition();&#039;&lt;br /&gt;
&lt;br /&gt;
definition() is used to define the elements in the form and &#039;&#039;&#039;this definition will be used for validating data submitted as well as for printing the form.&#039;&#039;&#039; For select and checkbox type elements only data that could have been selected will be allowed. And only data that corresponds to a form element in the defintion will be accepted as submitted data.&lt;br /&gt;
&lt;br /&gt;
The definition() should include all elements that are going to be used on form, some elements may be removed or tweaked later in definition_after_data(). Please do not create conditional elements in definition(), the definition() should not directly depend on the submitted data.&lt;br /&gt;
&lt;br /&gt;
==Use Fieldsets to group Form Elements==&lt;br /&gt;
&lt;br /&gt;
You use code like this to open a fieldset with a legend.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
//-------------------------------------------------------------------------------&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;header&#039;, &#039;nameforyourheaderelement&#039;, get_string(&#039;titleforlegened&#039;, &#039;modulename&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can&#039;t yet nest these visible fieldsets unfortunately. But in fact groups of elements are wrapped in invisible fieldsets.&lt;br /&gt;
&lt;br /&gt;
You close a fieldset with moodle_form&#039;s closeHeaderBefore method. You tell closeHeaderBefore the element before you wish to end the fieldset. A fieldset is automatically closed if you open a new one. You need to use this code only if you want to close a fieldset and the subsequent form elements are not to be enclosed by a visible fieldset (they are still enclosed with an invisibe one with no legend) :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;closeHeaderBefore(&#039;buttonar&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==addElement==&lt;br /&gt;
&lt;br /&gt;
Use the addElement method to add an element to a form. The first few arguments are always the same. The first param is the type of the element to add. The second is the elementname to use which is normally the html name of the element in the form. The third is often the text for the label for the element.&lt;br /&gt;
&lt;br /&gt;
Some examples are below :&lt;br /&gt;
===button===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            $mform-&amp;gt;addElement(&#039;button&#039;, &#039;intro&#039;, get_string(&amp;quot;buttonlabel&amp;quot;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A button element. If you want a submit or cancel button see &#039;submit&#039; element. &lt;br /&gt;
&lt;br /&gt;
===checkbox===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;checkbox&#039;, &#039;ratingtime&#039;, get_string(&#039;ratingtime&#039;, &#039;forum&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is a simple checkbox. The third param for this element is the label to display on the left side of the form. You can also supply a string as a fourth param to specify a label that will appear on the right of the element. Checkboxes and radio buttons can be grouped and have individual labels on their right.&lt;br /&gt;
&lt;br /&gt;
====advcheckbox====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;advcheckbox&#039;, &#039;ratingtime&#039;, get_string(&#039;ratingtime&#039;, &#039;forum&#039;), array(0, 1), array(&#039;group&#039; =&amp;gt; 1));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Similar to the checkbox above, but with a couple of important improvements:&lt;br /&gt;
&lt;br /&gt;
#The 4th parameter is an array of values that will be associated with the checked/unchecked state of the checkbox. With a normal checkbox you cannot choose that value, and in fact an unchecked checkbox will not even be sent with the form data.&lt;br /&gt;
#The 5th parameter is a normal $attributes array, normally used to set HTML attributes for the &amp;lt;input&amp;gt; element. However, a special value of &#039;group&#039; can be given, which will add a class name to the element, and enable its grouping for a [[lib/formslib.php_add_checkbox_controller|checkbox controller]]&lt;br /&gt;
&lt;br /&gt;
===choosecoursefile===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;choosecoursefile&#039;, &#039;mediafile&#039;, get_string(&#039;mediafile&#039;, &#039;lesson&#039;), array(&#039;courseid&#039;=&amp;gt;$COURSE-&amp;gt;id));&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Choose a file from the course files area. The fourth option is a list of options for the element. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
array(&#039;courseid&#039;=&amp;gt;null,\\if it is null (default then use globabl $COURSE&lt;br /&gt;
 &#039;height&#039;=&amp;gt;500,\\ height of the popup window&lt;br /&gt;
 &#039;width&#039;=&amp;gt;750, \\ width of the popup window&lt;br /&gt;
&#039;options&#039;=&amp;gt;&#039;none&#039;);\\options string for the pop up window &lt;br /&gt;
                   \\eg. &#039;menubar=0,location=0,scrollbars,resizable&#039;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===date_selector===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;date_selector&#039;, &#039;assesstimefinish&#039;, get_string(&#039;to&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is a date selector. You can select a Day, Month and Year using a group of select boxes. The fourth param here is an array of options. The defaults for the options are :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;array(&#039;startyear&#039;=&amp;gt;1970, &#039;stopyear&#039;=&amp;gt;2020,&lt;br /&gt;
                    &#039;timezone&#039;=&amp;gt;99, &#039;applydst&#039;=&amp;gt;true);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can override these defaults by supplying an array as fourth param with one or more keys with a value to override the default. You can supply a fifth param of attributes here as well.&lt;br /&gt;
&lt;br /&gt;
===date_time_selector===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;date_time_selector&#039;, &#039;assesstimestart&#039;, get_string(&#039;from&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is a group of select boxes to select a date (Day Month and Year) and time (Hour and Minute). When submitted, submitted data is processed and a timestamp is passed to $form-&amp;gt;get_data(); the fourth param here is an array of options. The defaults for the options are:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;array(&#039;startyear&#039;=&amp;gt;1970, &#039;stopyear&#039;=&amp;gt;2020,&lt;br /&gt;
                    &#039;timezone&#039;=&amp;gt;99, &#039;applydst&#039;=&amp;gt;true, &#039;step&#039;=&amp;gt;5);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can override these defaults by supplying an array as fourth param with one or more keys with a value to override the default. You can supply a fifth param of attributes here as well.&lt;br /&gt;
&lt;br /&gt;
===file===&lt;br /&gt;
&lt;br /&gt;
File upload input box with browse button. In the form definition type&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            $mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
after form submission and validation use&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
              ...&lt;br /&gt;
              $mform-&amp;gt;save_files($destination_directory);&lt;br /&gt;
              ...&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you need advanced settings such as required file, different max upload size or name of uploaded file&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
            $this-&amp;gt;set_upload_manager(new upload_manager(&#039;attachment&#039;, true, false, $COURSE, false, 0, true, true, false));&lt;br /&gt;
            $mform-&amp;gt;addElement(&#039;file&#039;, &#039;attachment&#039;, get_string(&#039;attachment&#039;, &#039;forum&#039;));&lt;br /&gt;
            $mform-&amp;gt;addRule(&#039;attachment&#039;, null, &#039;required&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            if ($data = $mform-&amp;gt;get_data()) {&lt;br /&gt;
              ...&lt;br /&gt;
              $mform-&amp;gt;save_files($destination_directory);&lt;br /&gt;
              $newfilename = $mform-&amp;gt;get_new_filename();&lt;br /&gt;
              ...&lt;br /&gt;
            }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When porting old code it is also possible to use the upload manager directly for processing of uploaded files.&lt;br /&gt;
&lt;br /&gt;
Please note that if using set_upload_manager() it must be before addElement(&#039;file&#039;,..).&lt;br /&gt;
&lt;br /&gt;
===hidden===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         $mform-&amp;gt;addElement(&#039;hidden&#039;, &#039;reply&#039;, &#039;yes&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A hidden element. Set the element name (in this case &#039;&#039;&#039;reply&#039;&#039;&#039;) to the stated value (in this case &#039;&#039;&#039;yes&#039;&#039;&#039;).&lt;br /&gt;
&lt;br /&gt;
===htmleditor &amp;amp; format===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;htmleditor&#039;, &#039;text&#039;, get_string(&#039;choicetext&#039;, &#039;choice&#039;));&lt;br /&gt;
        $mform-&amp;gt;setType(&#039;text&#039;, PARAM_RAW);&lt;br /&gt;
	$mform-&amp;gt;addRule(&#039;text&#039;, null, &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;format&#039;, &#039;format&#039;, get_string(&#039;format&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
You can supply a fourth param to htmleditor of an array of options :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
array(&#039;canUseHtmlEditor&#039;=&amp;gt;&#039;detect&#039;,&#039;rows&#039;=&amp;gt;10, &#039;cols&#039;=&amp;gt;65, &lt;br /&gt;
&#039;width&#039;=&amp;gt;0,&#039;height&#039;=&amp;gt;0, &#039;course&#039;=&amp;gt;0);&lt;br /&gt;
//options same as print_textarea params&lt;br /&gt;
//use rows and cols options to control htmleditor size.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===modgrade===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;modgrade&#039;, &#039;scale&#039;, get_string(&#039;grade&#039;), false);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This is a custom element for selecting a grade for any activity module. The fourth argument is whether to include an option for no grade which has a value 0. This select box does include scales. The default is true, include no grade option.&lt;br /&gt;
&lt;br /&gt;
A helpbutton is automatically added.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===password===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         $mform-&amp;gt;addElement(&#039;password&#039;, &#039;password&#039;, get_string(&#039;label&#039;), $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A password element. Fourth param is an array or string of attributes.&lt;br /&gt;
&lt;br /&gt;
===passwordunmask===&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         $mform-&amp;gt;addElement(&#039;passwordunmask&#039;, &#039;password&#039;, get_string(&#039;label&#039;), $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A password element with option to show the password in plaintext. Fourth param is an array or string of attributes.&lt;br /&gt;
&lt;br /&gt;
===radio===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $radioarray=array();&lt;br /&gt;
        $radioarray[] = &amp;amp;MoodleQuickForm::createElement(&#039;radio&#039;, &#039;yesno&#039;, &#039;&#039;, get_string(&#039;yes&#039;), 1, $attributes);&lt;br /&gt;
        $radioarray[] = &amp;amp;MoodleQuickForm::createElement(&#039;radio&#039;, &#039;yesno&#039;, &#039;&#039;, get_string(&#039;no&#039;), 0, $attributes);&lt;br /&gt;
        $mform-&amp;gt;addGroup($radioarray, &#039;radioar&#039;, &#039;&#039;, array(&#039; &#039;), false);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Second param names the radio button and should be the same for each button in the group in order to toggle correctly. Third param would be the label for the form element but is generally ignored as this element will always be in a group which has it&#039;s own label. Fourth param is a string, a label to be displayed on the right of the element. The fifth is the value for this radio button. $attributes can be a string or an array of attributes.&lt;br /&gt;
&lt;br /&gt;
====setDefault====&lt;br /&gt;
&lt;br /&gt;
To set the default for a radio button group as above use the following :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;setDefault(&#039;yesno&#039;, 0);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This would make the default &#039;no&#039;.&lt;br /&gt;
&lt;br /&gt;
===select===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;select&#039;, &#039;type&#039;, get_string(&#039;forumtype&#039;, &#039;forum&#039;), $FORUM_TYPES, $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The fourth param for this element is an array of options for the select box. The keys are the values for the option and the value of the array is the text for the option. The fifth param $attributes is optional, see text element for description of attributes param.&lt;br /&gt;
&lt;br /&gt;
====multi-select====&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $select = $mform-&amp;gt;addElement(&#039;select&#039;, &#039;colors&#039;, get_string(&#039;colors&#039;), array(&#039;red&#039;, &#039;blue&#039;, &#039;green&#039;), $attributes);&lt;br /&gt;
        $select-&amp;gt;setMultiple(true);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===selectyesno===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;selectyesno&#039;, &#039;maxbytes&#039;, get_string(&#039;maxattachmentsize&#039;, &#039;forum&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you want a yes / no select box this one automatically translates itself and has value 1 for yes and 0 for no.&lt;br /&gt;
&lt;br /&gt;
===static===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
         $mform-&amp;gt;addElement(&#039;static&#039;, &#039;description&#039;, get_string(&#039;description&#039;, &#039;exercise&#039;),&lt;br /&gt;
                  get_string(&#039;descriptionofexercise&#039;, &#039;exercise&#039;, $COURSE-&amp;gt;students));&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is a static element. It should be used with care it is used to display a static piece of text with a label. The third param is the label and the fourth is the static text itself.&lt;br /&gt;
&lt;br /&gt;
===submit, reset and cancel===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        //normally you use add_action_buttons instead of this code&lt;br /&gt;
        $buttonarray=array();&lt;br /&gt;
        $buttonarray[] = &amp;amp;$mform-&amp;gt;createElement(&#039;submit&#039;, &#039;submitbutton&#039;, get_string(&#039;savechanges&#039;));&lt;br /&gt;
        $buttonarray[] = &amp;amp;$mform-&amp;gt;createElement(&#039;reset&#039;, &#039;resetbutton&#039;, get_string(&#039;revert&#039;));&lt;br /&gt;
        $buttonarray[] = &amp;amp;$mform-&amp;gt;createElement(&#039;cancel&#039;);&lt;br /&gt;
        $mform-&amp;gt;addGroup($buttonarray, &#039;buttonar&#039;, &#039;&#039;, array(&#039; &#039;), false);&lt;br /&gt;
        $mform-&amp;gt;closeHeaderBefore(&#039;buttonar&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A &#039;Submit&#039; type element is a submit type form element which will submit the form. A &#039;Reset&#039; will not submit the form but will reset any changes the user has made to form contents. A &#039;Cancel&#039; element cancels form submission. You need to have a branch in your code before you check for get_data() to check if submission has been cancelled with is_cancelled(); See the example on the usage page.&lt;br /&gt;
&lt;br /&gt;
You should name your submit and reset buttons &#039;submitbutton&#039; and &#039;resetbutton&#039; or something similar (not &#039;submit&#039; and &#039;reset&#039;). This avoids problems in JavaScript of collisions between form element names and names of JavaScript methods of the form object.&lt;br /&gt;
&lt;br /&gt;
====add_action_buttons($cancel = true, $submitlabel=null);====&lt;br /&gt;
&lt;br /&gt;
You will normally use this helper function which is a method of moodleform to add all the &#039;action&#039; buttons to the end of your form. A boolean parameter allow you to specify whether to include a cancel button and specify the label for your submit button (pass the result of get_string). Default for the submit button label is get_string(&#039;savechanges&#039;).&lt;br /&gt;
&lt;br /&gt;
===text===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;name&#039;, get_string(&#039;forumname&#039;, &#039;forum&#039;), $attributes);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
For a simple text element. Your fourth parameter here can be a string or array of attributes for the text element. The following are equivalent :&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $attributes=&#039;size=&amp;quot;20&amp;quot;&#039;;&lt;br /&gt;
        $attributes=array(&#039;size&#039;=&amp;gt;&#039;20&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
Generally you are encouraged to use CSS instead of using attributes for styling.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
A format element can be used as a format select box. It will be non-selectable if you&#039;re using an html editor.&lt;br /&gt;
&lt;br /&gt;
The third param for this element is $useHtmlEditor and it defaults to null in which case an html editor is used if the browser and user profile support it.&lt;br /&gt;
&lt;br /&gt;
===textarea===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            $mform-&amp;gt;addElement(&#039;textarea&#039;, &#039;introduction&#039;, get_string(&amp;quot;introtext&amp;quot;, &amp;quot;survey&amp;quot;), &#039;wrap=&amp;quot;virtual&amp;quot; rows=&amp;quot;20&amp;quot; cols=&amp;quot;50&amp;quot;&#039;);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
A textarea element. If you want an htmleditor use htmleditor element. Fourth element here is a string or array of attributes.&lt;br /&gt;
&lt;br /&gt;
==addGroup==&lt;br /&gt;
&lt;br /&gt;
A &#039;group&#039; in formslib is just a group of elements that will have a label and will be included on one line. &lt;br /&gt;
&lt;br /&gt;
For example typical code to include a submit and cancel button on the same line : &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $buttonarray=array();&lt;br /&gt;
        $buttonarray[] =&amp;amp; $mform-&amp;gt;createElement(&#039;submit&#039;, &#039;submitbutton&#039;, get_string(&#039;savechanges&#039;));&lt;br /&gt;
        $buttonarray[] =&amp;amp; $mform-&amp;gt;createElement(&#039;submit&#039;, &#039;cancel&#039;, get_string(&#039;cancel&#039;));&lt;br /&gt;
        $mform-&amp;gt;addGroup($buttonarray, &#039;buttonar&#039;, &#039;&#039;, array(&#039; &#039;), false);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You use the same arguments for createElement as you do for addElement. Any label for the element in the third argument is normally ignored (but not in the case of the submit buttons above where the third argument is not for a label but is the text for the button).&lt;br /&gt;
&lt;br /&gt;
Here&#039;s an example of putting a date_selector (which is itself a group of elements) and a checkbox on the same line, note that you can disable every element in the group using the group name &#039;availablefromgroup&#039; but it doesn&#039;t disable the controlling element the &#039;availablefromenabled&#039; checkbox:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $availablefromgroup=array();&lt;br /&gt;
	$availablefromgroup[] =&amp;amp; $mform-&amp;gt;createElement(&#039;date_selector&#039;, &#039;availablefrom&#039;, &#039;&#039;);&lt;br /&gt;
	$availablefromgroup[] =&amp;amp; $mform-&amp;gt;createElement(&#039;checkbox&#039;, &#039;availablefromenabled&#039;, &#039;&#039;, get_string(&#039;enable&#039;));&lt;br /&gt;
        $mform-&amp;gt;addGroup($availablefromgroup, &#039;availablefromgroup&#039;, get_string(&#039;availablefromdate&#039;, &#039;data&#039;), &#039;&amp;amp;nbsp;&#039;, false);&lt;br /&gt;
        $mform-&amp;gt;disabledIf(&#039;availablefromgroup&#039;, &#039;availablefromenabled&#039;);&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==setHelpButton==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
            $mform-&amp;gt;setHelpButton(&#039;lessondefault&#039;, array(&#039;lessondefault&#039;, get_string(&#039;lessondefault&#039;, &#039;lesson&#039;), &#039;lesson&#039;));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
First param is an elementname and the second param is an array of params that are passed to helpbutton in weblib.php. Params are :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 * @param string $page  The keyword that defines a help page&lt;br /&gt;
 * @param string $title The title of links, rollover tips, alt tags etc&lt;br /&gt;
 *           &#039;Help with&#039; (or the language equivalent) will be prefixed and &#039;...&#039; will be stripped.&lt;br /&gt;
 * @param string $module Which module is the page defined in&lt;br /&gt;
 * @param mixed $image Use a help image for the link?  (true/false/&amp;quot;both&amp;quot;)&lt;br /&gt;
 * @param boolean $linktext If true, display the title next to the help icon.&lt;br /&gt;
 * @param string $text If defined then this text is used in the page, and&lt;br /&gt;
 *           the $page variable is ignored.&lt;br /&gt;
 * @param boolean $return If true then the output is returned as a string, if false it is printed to the current page.&lt;br /&gt;
 * @param string $imagetext The full text for the helpbutton icon. If empty use default help.gif&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Make sure you don&#039;t set boolean $return to false. &lt;br /&gt;
&lt;br /&gt;
You need to do use this method after addElement();&lt;br /&gt;
&lt;br /&gt;
==setDefault==&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;select&#039;, &#039;grade&#039;, get_string(&#039;gradeforsubmission&#039;, &#039;exercise&#039;), $grades);&lt;br /&gt;
        $mform-&amp;gt;setHelpButton(&#039;grade&#039;, array(&#039;grade&#039;, get_string(&#039;gradeforsubmission&#039;, &#039;exercise&#039;), &#039;exercise&#039;));&lt;br /&gt;
        $mform-&amp;gt;setDefault(&#039;grade&#039;, 100);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Set the default of the form value with setDefault($elementname, $value); where elementname is the elementname whose default you want to set and $value is the default to set. We set the defaults for the form in definition(). This default is what is used if no data is loaded into the form with set_data(); eg. on display of the form for an &#039;add&#039; rather than &#039;update&#039; function.&lt;br /&gt;
&lt;br /&gt;
==disabledIf==&lt;br /&gt;
&lt;br /&gt;
For any element or groups of element in a form you can conditionally disable the group or individual element depending on conditions.&lt;br /&gt;
&lt;br /&gt;
You can use $mform-&amp;gt;disabledIf($elementName, $dependentOn, $condition = &#039;notchecked&#039;, $value=null)&lt;br /&gt;
&lt;br /&gt;
* elementname can be a group. If you specify a group all elements in the group will be disabled (if dependentOn is in elementname group that is ignored and not disabled). These are the element names you&#039;ve used as the first argument in addElement or addGroup.&lt;br /&gt;
* dependentOn is the actual name of the element as it will appear in html. This can be different to the name used in addGroup particularly but also addElement where you&#039;re adding a complex element like a date_selector. Check the html of your page. You typically make the depedentOn a checkbox or select box.&lt;br /&gt;
* $condition will be notchecked, checked, selected, eq or if it is anything else then we test for neq.&lt;br /&gt;
* If $condition is eq or neq then we check the value of the dependentOn field and check for equality (==) or nonequality (!=) in js&lt;br /&gt;
* If $condition is checked or notchecked then we check to see if a checkbox is checked or not.&lt;br /&gt;
* If $condition is selected then we check to see if a particular option is selected from a dropdown list.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note: I am not sure this section is complete. I just found and added one missing case, but there may be others--[[User:Tim Hunt|Tim Hunt]] 06:04, 15 October 2007 (CDT)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==setType==&lt;br /&gt;
&lt;br /&gt;
PARAM_* types are used to specify how a submitted variable should be cleaned. These should be used for get parameters such as id, course etc. which are used to load a page and also with setType(); method. Every form element should have a type specified except select, radio box and checkbox elements, these elements do a good job of cleaning themselves (only specified options are allowed as user input).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Most Commonly Used PARAM_* Types===&lt;br /&gt;
&lt;br /&gt;
These are the most commonly used PARAM_* types and their proper uses. More types can be seen in moodlelib.php starting around line 100.&lt;br /&gt;
&lt;br /&gt;
* PARAM_CLEAN is deprecated and you should try to use a more specific type.&lt;br /&gt;
* PARAM_TEXT should be used for cleaning data that is expected to be plain text. It will strip all html tags. But will still let tags for multilang support through.&lt;br /&gt;
* PARAM_NOTAGS should be used for cleaning data that is expected to be plain text. It will strip *all* html type tags. It will still *not* let tags for multilang support through. This should be used for instance for email addresses where no multilang support is appropriate.&lt;br /&gt;
* PARAM_RAW means no cleaning whatsoever, it is used mostly for data from the html editor. Data from the editor is later cleaned before display using format_text() function. PARAM_RAW can also be used for data that is validated by some other way or printed by p() or s().&lt;br /&gt;
* PARAM_INT should be used for integers.&lt;br /&gt;
* PARAM_ACTION is an alias of PARAM_ALPHA and is used for hidden fields specifying form actions.&lt;br /&gt;
&lt;br /&gt;
==References==&lt;br /&gt;
&lt;br /&gt;
* [http://midnighthax.com/quickform.php PEAR HTML QuickForm Getting Started Guide by Keith Edmunds of Midnighthax.com]&lt;br /&gt;
&lt;br /&gt;
[[Category:Formslib]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6538</id>
		<title>Old Events API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6538"/>
		<updated>2008-04-14T09:18:08Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Events wishlist */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Events API is a new core system in Moodle to allow better communication between modules.  It&#039;s based on modules triggering new events with attached data, and the other modules handling those events with custom functions.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
We&#039;ll be using the example of a grade being posted from a module into the [[Grades|new gradebook in Moodle 1.9]], but there are obviously all kinds of events possible.&lt;br /&gt;
&lt;br /&gt;
===Triggering an event===&lt;br /&gt;
&lt;br /&gt;
Whenever a grade is created or changed by a module, it should “tell” the system about it (in addition to any local working storage it uses).   So, using the quiz as an example, we first define an object as follows:&lt;br /&gt;
&lt;br /&gt;
 $eventdata = new object();&lt;br /&gt;
 $eventdata-&amp;gt;itemid = $grade_item-&amp;gt;id;&lt;br /&gt;
 $eventdata-&amp;gt;userid = $USER-&amp;gt;id;&lt;br /&gt;
 $eventdata-&amp;gt;gradevalue = $currentvalue;&lt;br /&gt;
&lt;br /&gt;
Then we post the object as an event and forget about it:&lt;br /&gt;
&lt;br /&gt;
 events_trigger(&#039;grade_updated&#039;, $eventdata);&lt;br /&gt;
&lt;br /&gt;
===Handling an event===&lt;br /&gt;
&lt;br /&gt;
Modules can define an events.php in their db directory which defines events they want to be notified about, and describes which of their functions or class methods should be notified.   For example, an export  plugin could register something like:&lt;br /&gt;
&lt;br /&gt;
 $handlers = array (&lt;br /&gt;
     &#039;grade_updated&#039; =&amp;gt; array (&lt;br /&gt;
         &#039;handlerfile&#039;      =&amp;gt; &#039;/grade/export/banner/lib.php&#039;,&lt;br /&gt;
         &#039;handlerfunction&#039;  =&amp;gt; &#039;banner_handle_grade_test&#039;,    // argument to call_user_func(), could be an array&lt;br /&gt;
         &#039;schedule&#039;         =&amp;gt; &#039;cron&#039;&lt;br /&gt;
     ) &lt;br /&gt;
 );&lt;br /&gt;
These are parsed during install / upgrade and stored in a simple database table.&lt;br /&gt;
&lt;br /&gt;
Then, when a grade_added event happens, all the registered functions for that event will be called something like this (but with more error handling):&lt;br /&gt;
&lt;br /&gt;
          include_once($CFG-&amp;gt;dirroot.$handlers[&#039;grade_updated&#039;][&#039;file&#039;]);&lt;br /&gt;
          call_user_func($handlers[&#039;grade_updated&#039;][&#039;function&#039;], $eventdata);&lt;br /&gt;
&lt;br /&gt;
All plugins in Moodle have access to this and can this easily “hook in” to &#039;grade_updated&#039; events (and of course any other events).&lt;br /&gt;
&lt;br /&gt;
==Database structure==&lt;br /&gt;
&lt;br /&gt;
There are 3 core tables for events. Note that if a handler is queued, and yet to be processed or processing failed, then all subsequent calls on that handler must be queued.&lt;br /&gt;
&lt;br /&gt;
===events_handlers===&lt;br /&gt;
&lt;br /&gt;
This table is for storing which components requests what type of event, and the location of the responsible handlers. For example, the grade book can register &#039;grade_added&#039; event with a function add_grade() that should be called any time an &#039;grade_added&#039; event is triggered by a module.&lt;br /&gt;
&lt;br /&gt;
These entries are created by parsing events.php files in all the modules, and can be rebuilt any time (during an upgrade, say).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventname&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of the event, e.g. &#039;grade_updated&#039;&lt;br /&gt;
|-&lt;br /&gt;
|handlermodule&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|e.g. moodle, mod/forum, block/rss_client&lt;br /&gt;
|-&lt;br /&gt;
|handlerfile&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|path to the file of the function, eg /grade/export/lib.php&lt;br /&gt;
|-&lt;br /&gt;
|handlerfunction&lt;br /&gt;
|text&lt;br /&gt;
|serialized string or array describing function, suitable to be passed to &#039;&#039;&#039;call_user_func()&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|schedule 	&lt;br /&gt;
|varchar(255) 	&lt;br /&gt;
|&#039;cron&#039; or &#039;instant&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue===&lt;br /&gt;
&lt;br /&gt;
This table is for storing queued events. It stores only one copy of the eventdata here, and entries from this table are being references by the events_queue_handlers table.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventdata 	&lt;br /&gt;
|longtext 	&lt;br /&gt;
|serialized version of the data object passed to the event handler.&lt;br /&gt;
|-&lt;br /&gt;
|stackdump&lt;br /&gt;
|text&lt;br /&gt;
|serialized debug_backtrace showing where the event was fired from&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|int(10)&lt;br /&gt;
|$USER-&amp;gt;id when the event was fired&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) 	&lt;br /&gt;
|time stamp of the first time this was added&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue_handlers===&lt;br /&gt;
&lt;br /&gt;
This is the list of queued handlers for processing. The event object is retrieved from the events_queue table. When no further reference is made to the events_queue table, the corresponding entry in the events_queue table should be deleted. Entry should get deleted (?) after a successful event processing by the specified handler.  The status field keeps track of failures, after it gets to a certain number (eg 10?) it should trigger an &amp;quot;event failed&amp;quot; event (that could result in admin being emailed etc, or perhaps even the originating module taking care of it or rolling something back etc).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|queuedeventid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_queues table&lt;br /&gt;
|-&lt;br /&gt;
|handlerid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_handlers table&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|-&lt;br /&gt;
|errormessage&lt;br /&gt;
|text&lt;br /&gt;
|if an error happened last time we tried to process this event, record it here.&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|time stamp of the last attempt to run this from the queue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Standards for naming events==&lt;br /&gt;
&lt;br /&gt;
All event names should follow a consistent naming pattern, such as modulename_noun_verb&lt;br /&gt;
&lt;br /&gt;
==Events which exist==&lt;br /&gt;
&lt;br /&gt;
===Users===&lt;br /&gt;
* user_updated&lt;br /&gt;
* password_changed&lt;br /&gt;
&lt;br /&gt;
===Courses===&lt;br /&gt;
* course_updated&lt;br /&gt;
* course_deleted&lt;br /&gt;
* category_updated&lt;br /&gt;
* category_deleted&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
* group_deleted&lt;br /&gt;
* grouping_deleted&lt;br /&gt;
* group_user_added&lt;br /&gt;
* group_user_removed&lt;br /&gt;
&lt;br /&gt;
==Events wishlist==&lt;br /&gt;
&lt;br /&gt;
List of events which it would be nice to have.  Please add to this list if what you want is not shown here.&lt;br /&gt;
&lt;br /&gt;
* user_created (for example to handle custom emails.  this is commonly desired: e.g. send a custom email to a related person (teacher, boss, etc.) based on some institution-specific logic)&lt;br /&gt;
* user_enrolled_in_course&lt;br /&gt;
* user_unenrolled_from_course&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=69103 General Developer Forum thread for discussing this proposal]. &lt;br /&gt;
* [[:Development:Grades]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Events]]&lt;br /&gt;
[[Category:Grades]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6537</id>
		<title>Old Events API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6537"/>
		<updated>2008-04-04T07:20:07Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Events wishlist */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Events API is a new core system in Moodle to allow better communication between modules.  It&#039;s based on modules triggering new events with attached data, and the other modules handling those events with custom functions.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
We&#039;ll be using the example of a grade being posted from a module into the [[Grades|new gradebook in Moodle 1.9]], but there are obviously all kinds of events possible.&lt;br /&gt;
&lt;br /&gt;
===Triggering an event===&lt;br /&gt;
&lt;br /&gt;
Whenever a grade is created or changed by a module, it should “tell” the system about it (in addition to any local working storage it uses).   So, using the quiz as an example, we first define an object as follows:&lt;br /&gt;
&lt;br /&gt;
 $eventdata = new object();&lt;br /&gt;
 $eventdata-&amp;gt;itemid = $grade_item-&amp;gt;id;&lt;br /&gt;
 $eventdata-&amp;gt;userid = $USER-&amp;gt;id;&lt;br /&gt;
 $eventdata-&amp;gt;gradevalue = $currentvalue;&lt;br /&gt;
&lt;br /&gt;
Then we post the object as an event and forget about it:&lt;br /&gt;
&lt;br /&gt;
 events_trigger(&#039;grade_updated&#039;, $eventdata);&lt;br /&gt;
&lt;br /&gt;
===Handling an event===&lt;br /&gt;
&lt;br /&gt;
Modules can define an events.php in their db directory which defines events they want to be notified about, and describes which of their functions or class methods should be notified.   For example, an export  plugin could register something like:&lt;br /&gt;
&lt;br /&gt;
 $handlers = array (&lt;br /&gt;
     &#039;grade_updated&#039; =&amp;gt; array (&lt;br /&gt;
         &#039;handlerfile&#039;      =&amp;gt; &#039;/grade/export/banner/lib.php&#039;,&lt;br /&gt;
         &#039;handlerfunction&#039;  =&amp;gt; &#039;banner_handle_grade_test&#039;,    // argument to call_user_func(), could be an array&lt;br /&gt;
         &#039;schedule&#039;         =&amp;gt; &#039;cron&#039;&lt;br /&gt;
     ) &lt;br /&gt;
 );&lt;br /&gt;
These are parsed during install / upgrade and stored in a simple database table.&lt;br /&gt;
&lt;br /&gt;
Then, when a grade_added event happens, all the registered functions for that event will be called something like this (but with more error handling):&lt;br /&gt;
&lt;br /&gt;
          include_once($CFG-&amp;gt;dirroot.$handlers[&#039;grade_updated&#039;][&#039;file&#039;]);&lt;br /&gt;
          call_user_func($handlers[&#039;grade_updated&#039;][&#039;function&#039;], $eventdata);&lt;br /&gt;
&lt;br /&gt;
All plugins in Moodle have access to this and can this easily “hook in” to &#039;grade_updated&#039; events (and of course any other events).&lt;br /&gt;
&lt;br /&gt;
==Database structure==&lt;br /&gt;
&lt;br /&gt;
There are 3 core tables for events. Note that if a handler is queued, and yet to be processed or processing failed, then all subsequent calls on that handler must be queued.&lt;br /&gt;
&lt;br /&gt;
===events_handlers===&lt;br /&gt;
&lt;br /&gt;
This table is for storing which components requests what type of event, and the location of the responsible handlers. For example, the grade book can register &#039;grade_added&#039; event with a function add_grade() that should be called any time an &#039;grade_added&#039; event is triggered by a module.&lt;br /&gt;
&lt;br /&gt;
These entries are created by parsing events.php files in all the modules, and can be rebuilt any time (during an upgrade, say).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventname&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of the event, e.g. &#039;grade_updated&#039;&lt;br /&gt;
|-&lt;br /&gt;
|handlermodule&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|e.g. moodle, mod/forum, block/rss_client&lt;br /&gt;
|-&lt;br /&gt;
|handlerfile&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|path to the file of the function, eg /grade/export/lib.php&lt;br /&gt;
|-&lt;br /&gt;
|handlerfunction&lt;br /&gt;
|text&lt;br /&gt;
|serialized string or array describing function, suitable to be passed to &#039;&#039;&#039;call_user_func()&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|schedule 	&lt;br /&gt;
|varchar(255) 	&lt;br /&gt;
|&#039;cron&#039; or &#039;instant&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue===&lt;br /&gt;
&lt;br /&gt;
This table is for storing queued events. It stores only one copy of the eventdata here, and entries from this table are being references by the events_queue_handlers table.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventdata 	&lt;br /&gt;
|longtext 	&lt;br /&gt;
|serialized version of the data object passed to the event handler.&lt;br /&gt;
|-&lt;br /&gt;
|stackdump&lt;br /&gt;
|text&lt;br /&gt;
|serialized debug_backtrace showing where the event was fired from&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|int(10)&lt;br /&gt;
|$USER-&amp;gt;id when the event was fired&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) 	&lt;br /&gt;
|time stamp of the first time this was added&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue_handlers===&lt;br /&gt;
&lt;br /&gt;
This is the list of queued handlers for processing. The event object is retrieved from the events_queue table. When no further reference is made to the events_queue table, the corresponding entry in the events_queue table should be deleted. Entry should get deleted (?) after a successful event processing by the specified handler.  The status field keeps track of failures, after it gets to a certain number (eg 10?) it should trigger an &amp;quot;event failed&amp;quot; event (that could result in admin being emailed etc, or perhaps even the originating module taking care of it or rolling something back etc).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|queuedeventid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_queues table&lt;br /&gt;
|-&lt;br /&gt;
|handlerid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_handlers table&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|-&lt;br /&gt;
|errormessage&lt;br /&gt;
|text&lt;br /&gt;
|if an error happened last time we tried to process this event, record it here.&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|time stamp of the last attempt to run this from the queue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Standards for naming events==&lt;br /&gt;
&lt;br /&gt;
All event names should follow a consistent naming pattern, such as modulename_noun_verb&lt;br /&gt;
&lt;br /&gt;
==Events which exist==&lt;br /&gt;
&lt;br /&gt;
===Users===&lt;br /&gt;
* user_updated&lt;br /&gt;
* password_changed&lt;br /&gt;
&lt;br /&gt;
===Courses===&lt;br /&gt;
* course_updated&lt;br /&gt;
* course_deleted&lt;br /&gt;
* category_updated&lt;br /&gt;
* category_deleted&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
* group_deleted&lt;br /&gt;
* grouping_deleted&lt;br /&gt;
* group_user_added&lt;br /&gt;
* group_user_removed&lt;br /&gt;
&lt;br /&gt;
==Events wishlist==&lt;br /&gt;
&lt;br /&gt;
List of events which it would be nice to have.  Please add to this list if what you want is not shown here.&lt;br /&gt;
&lt;br /&gt;
* user_created (for example to handle custom emails.  this is commonly desired: e.g. send a custom email to a related person (teacher, boss, etc.) based on some institution-specific logic)&lt;br /&gt;
* user_enrolled_in_course&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=69103 General Developer Forum thread for discussing this proposal]. &lt;br /&gt;
* [[:Development:Grades]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Events]]&lt;br /&gt;
[[Category:Grades]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6536</id>
		<title>Old Events API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Old_Events_API&amp;diff=6536"/>
		<updated>2008-04-03T12:48:01Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Standards for naming events */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Events API is a new core system in Moodle to allow better communication between modules.  It&#039;s based on modules triggering new events with attached data, and the other modules handling those events with custom functions.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
We&#039;ll be using the example of a grade being posted from a module into the [[Grades|new gradebook in Moodle 1.9]], but there are obviously all kinds of events possible.&lt;br /&gt;
&lt;br /&gt;
===Triggering an event===&lt;br /&gt;
&lt;br /&gt;
Whenever a grade is created or changed by a module, it should “tell” the system about it (in addition to any local working storage it uses).   So, using the quiz as an example, we first define an object as follows:&lt;br /&gt;
&lt;br /&gt;
 $eventdata = new object();&lt;br /&gt;
 $eventdata-&amp;gt;itemid = $grade_item-&amp;gt;id;&lt;br /&gt;
 $eventdata-&amp;gt;userid = $USER-&amp;gt;id;&lt;br /&gt;
 $eventdata-&amp;gt;gradevalue = $currentvalue;&lt;br /&gt;
&lt;br /&gt;
Then we post the object as an event and forget about it:&lt;br /&gt;
&lt;br /&gt;
 events_trigger(&#039;grade_updated&#039;, $eventdata);&lt;br /&gt;
&lt;br /&gt;
===Handling an event===&lt;br /&gt;
&lt;br /&gt;
Modules can define an events.php in their db directory which defines events they want to be notified about, and describes which of their functions or class methods should be notified.   For example, an export  plugin could register something like:&lt;br /&gt;
&lt;br /&gt;
 $handlers = array (&lt;br /&gt;
     &#039;grade_updated&#039; =&amp;gt; array (&lt;br /&gt;
         &#039;handlerfile&#039;      =&amp;gt; &#039;/grade/export/banner/lib.php&#039;,&lt;br /&gt;
         &#039;handlerfunction&#039;  =&amp;gt; &#039;banner_handle_grade_test&#039;,    // argument to call_user_func(), could be an array&lt;br /&gt;
         &#039;schedule&#039;         =&amp;gt; &#039;cron&#039;&lt;br /&gt;
     ) &lt;br /&gt;
 );&lt;br /&gt;
These are parsed during install / upgrade and stored in a simple database table.&lt;br /&gt;
&lt;br /&gt;
Then, when a grade_added event happens, all the registered functions for that event will be called something like this (but with more error handling):&lt;br /&gt;
&lt;br /&gt;
          include_once($CFG-&amp;gt;dirroot.$handlers[&#039;grade_updated&#039;][&#039;file&#039;]);&lt;br /&gt;
          call_user_func($handlers[&#039;grade_updated&#039;][&#039;function&#039;], $eventdata);&lt;br /&gt;
&lt;br /&gt;
All plugins in Moodle have access to this and can this easily “hook in” to &#039;grade_updated&#039; events (and of course any other events).&lt;br /&gt;
&lt;br /&gt;
==Database structure==&lt;br /&gt;
&lt;br /&gt;
There are 3 core tables for events. Note that if a handler is queued, and yet to be processed or processing failed, then all subsequent calls on that handler must be queued.&lt;br /&gt;
&lt;br /&gt;
===events_handlers===&lt;br /&gt;
&lt;br /&gt;
This table is for storing which components requests what type of event, and the location of the responsible handlers. For example, the grade book can register &#039;grade_added&#039; event with a function add_grade() that should be called any time an &#039;grade_added&#039; event is triggered by a module.&lt;br /&gt;
&lt;br /&gt;
These entries are created by parsing events.php files in all the modules, and can be rebuilt any time (during an upgrade, say).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventname&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of the event, e.g. &#039;grade_updated&#039;&lt;br /&gt;
|-&lt;br /&gt;
|handlermodule&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|e.g. moodle, mod/forum, block/rss_client&lt;br /&gt;
|-&lt;br /&gt;
|handlerfile&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|path to the file of the function, eg /grade/export/lib.php&lt;br /&gt;
|-&lt;br /&gt;
|handlerfunction&lt;br /&gt;
|text&lt;br /&gt;
|serialized string or array describing function, suitable to be passed to &#039;&#039;&#039;call_user_func()&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|schedule 	&lt;br /&gt;
|varchar(255) 	&lt;br /&gt;
|&#039;cron&#039; or &#039;instant&#039;.&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue===&lt;br /&gt;
&lt;br /&gt;
This table is for storing queued events. It stores only one copy of the eventdata here, and entries from this table are being references by the events_queue_handlers table.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|eventdata 	&lt;br /&gt;
|longtext 	&lt;br /&gt;
|serialized version of the data object passed to the event handler.&lt;br /&gt;
|-&lt;br /&gt;
|stackdump&lt;br /&gt;
|text&lt;br /&gt;
|serialized debug_backtrace showing where the event was fired from&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|int(10)&lt;br /&gt;
|$USER-&amp;gt;id when the event was fired&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) 	&lt;br /&gt;
|time stamp of the first time this was added&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===events_queue_handlers===&lt;br /&gt;
&lt;br /&gt;
This is the list of queued handlers for processing. The event object is retrieved from the events_queue table. When no further reference is made to the events_queue table, the corresponding entry in the events_queue table should be deleted. Entry should get deleted (?) after a successful event processing by the specified handler.  The status field keeps track of failures, after it gets to a certain number (eg 10?) it should trigger an &amp;quot;event failed&amp;quot; event (that could result in admin being emailed etc, or perhaps even the originating module taking care of it or rolling something back etc).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|- &lt;br /&gt;
|id&lt;br /&gt;
|int(10)&lt;br /&gt;
|auto increment identifier&lt;br /&gt;
|-&lt;br /&gt;
|queuedeventid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_queues table&lt;br /&gt;
|-&lt;br /&gt;
|handlerid&lt;br /&gt;
|int(10)&lt;br /&gt;
|foreign key id corresponding to the id of the event_handlers table&lt;br /&gt;
|-&lt;br /&gt;
|status&lt;br /&gt;
|int(10)&lt;br /&gt;
|number of failed attempts to process this handler&lt;br /&gt;
|-&lt;br /&gt;
|errormessage&lt;br /&gt;
|text&lt;br /&gt;
|if an error happened last time we tried to process this event, record it here.&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|time stamp of the last attempt to run this from the queue&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Standards for naming events==&lt;br /&gt;
&lt;br /&gt;
All event names should follow a consistent naming pattern, such as modulename_noun_verb&lt;br /&gt;
&lt;br /&gt;
==Events which exist==&lt;br /&gt;
&lt;br /&gt;
===Users===&lt;br /&gt;
* user_updated&lt;br /&gt;
* password_changed&lt;br /&gt;
&lt;br /&gt;
===Courses===&lt;br /&gt;
* course_updated&lt;br /&gt;
* course_deleted&lt;br /&gt;
* category_updated&lt;br /&gt;
* category_deleted&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
* group_deleted&lt;br /&gt;
* grouping_deleted&lt;br /&gt;
* group_user_added&lt;br /&gt;
* group_user_removed&lt;br /&gt;
&lt;br /&gt;
==Events wishlist==&lt;br /&gt;
&lt;br /&gt;
List of events which it would be nice to have.  Please add to this list if what you want is not shown here.&lt;br /&gt;
&lt;br /&gt;
* user_created (for example to handle custom emails.  this is commonly desired: e.g. send a custom email to a related person (teacher, boss, etc.) based on some institution-specific logic)&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=69103 General Developer Forum thread for discussing this proposal]. &lt;br /&gt;
* [[:Development:Grades]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Events]]&lt;br /&gt;
[[Category:Grades]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=MNet&amp;diff=6105</id>
		<title>MNet</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=MNet&amp;diff=6105"/>
		<updated>2007-02-06T14:24:50Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Key config variables and functions */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 1.8}}&lt;br /&gt;
&lt;br /&gt;
Random notes for Moodle Network. Should get these organised...&lt;br /&gt;
&lt;br /&gt;
* Documentation for administrators is here [[Moodle Network]]&lt;br /&gt;
* Some old documentation about the initial planning and dev&lt;br /&gt;
** [[Community_hub]]&lt;br /&gt;
** [[Community_hub_technotes]]&lt;br /&gt;
** [[Community_hub_progress]]&lt;br /&gt;
&lt;br /&gt;
=Key config variables and functions=&lt;br /&gt;
&lt;br /&gt;
     // what mnethostid should &amp;quot;local&amp;quot; users have?&lt;br /&gt;
     // this is guaranteed to be set, except during the 1.7-&amp;gt;1.8 upgrade&lt;br /&gt;
     $id = $CFG-&amp;gt;mnet_localhost_id; &lt;br /&gt;
     &lt;br /&gt;
     // a quick way to check if our user is local&lt;br /&gt;
     is_mnet_remote_user($user); &lt;br /&gt;
     // internally wis_mnet_remote_user() does&lt;br /&gt;
     // if ($user-&amp;gt;mnethostid == $CFG-&amp;gt;mnet_localhost_id)&lt;br /&gt;
&lt;br /&gt;
     // Are we listening to mnet requests? Nothing else works if we aren&#039;t.&lt;br /&gt;
     if ($CFG-&amp;gt;mnet_dispatcher_mode === &#039;strict&#039;) {&lt;br /&gt;
         // Moodle is listening to mnet requests. Nothing else works if we aren&#039;t.&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
     // Are we allowing remote users in via auth/mnet?&lt;br /&gt;
     if (is_enabled_auth(&#039;mnet&#039;)) {&lt;br /&gt;
          // yes indeed&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
     // Will auth/mnet autocreate new user accts?&lt;br /&gt;
     if (get_config(&#039;auth/mnet&#039;, &#039;auto_add_remote_users&#039;)) {&lt;br /&gt;
         // yes it will!&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
=Protocols=&lt;br /&gt;
&lt;br /&gt;
==MNET handshake==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Authentication/SSO==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Enrolment==&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=To Do=&lt;br /&gt;
&lt;br /&gt;
Some immediate items that would add polish without major surgery&lt;br /&gt;
&lt;br /&gt;
* Exchange more enrolment info at log-entry exchange time on cron&lt;br /&gt;
* Special default role&lt;br /&gt;
** Add a new &amp;quot;remote student&amp;quot; role&lt;br /&gt;
** Add a config var to enrol/mnet: &amp;quot;defaultremoterole&amp;quot; to use &amp;quot;remote student&amp;quot;&lt;br /&gt;
** Add a config var to the courses table to optionally override defaultremoterole&lt;br /&gt;
* Nicer CSS in the &amp;quot;my courses&amp;quot; listing in moodle homepage for remote hosts and remote courses. See course/lib.php print_remote_course() and print_remote_host().&lt;br /&gt;
&lt;br /&gt;
=Roadmap=&lt;br /&gt;
&lt;br /&gt;
A bit more work, and a whole lot more features. From easy to hard...&lt;br /&gt;
&lt;br /&gt;
* Better per-host stats and log views for administrators. This is relatively easy using data we already have.&lt;br /&gt;
* In mod/forum craft a special URL for remote users so that the post URL is in the wantsurl parameter, so they can bounce-off their IDP in one go. &lt;br /&gt;
** According to Jonathan Harker, this requires that we modify jump to accept the remote wwwroot as a parameter. Also: will this be safe enough?&lt;br /&gt;
* Display aggregated calendar on the IDP&lt;br /&gt;
* Exchange grades. Needs a bit of design.&lt;br /&gt;
* Build a SCORM/IMSCP/MoodleZip repository scheme.&lt;br /&gt;
** MartinL has some ideas on how to get this done in easy and super-scalable.&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3489</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3489"/>
		<updated>2007-02-02T11:45:14Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Please also see the discussion about a simple to use xml-rpc based api at [[Simple web services|simple web services]].&lt;br /&gt;
&lt;br /&gt;
Provide Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
Specifically,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
The API will be built in two tiers:&lt;br /&gt;
# Generic Moodle web services - This will define the API available to be used by specific protocols.&lt;br /&gt;
# Protocol-specific web services - Using the generic services as the parent, define specific interfaces for SOAP, XML-RPC and others.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Class Structure:&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Base class &amp;lt;code&amp;gt;server.class.php&amp;lt;/code&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
:This class provides most of the necessary functions to authenticate and communicate with Moodle. It also defines the methods that must be implemented by the protocol class. Any web service protocol class must:&lt;br /&gt;
:* call the base class constructor (&amp;lt;code&amp;gt;parent::init()&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the main method (&amp;lt;code&amp;gt;main([$httpdata])&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the request method (&amp;lt;code&amp;gt;request($input)&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the reply method (&amp;lt;code&amp;gt;response($request)&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Derived class (e.g. &amp;lt;code&amp;gt;soapserver.class.php&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The driver for the whole operation is through &amp;lt;code&amp;gt;service.php&amp;lt;/code&amp;gt;. It takes one argument, &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt;, which contains a string identifying the protocol to use (e.g. &#039;soap&#039;).&lt;br /&gt;
&lt;br /&gt;
So, to call a SOAP based web service:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;http://your.server.org/moodle/ws/server.php?type=soap&amp;lt;/code&amp;gt;&lt;br /&gt;
The data for the entire operation comes through the POST mechanism.&lt;br /&gt;
&lt;br /&gt;
== Manage User Data ==&lt;br /&gt;
These functions will allow the exchange of user data, and account management functions. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Course Enrolments ==&lt;br /&gt;
These functions will allow for student, teacher and group management functions within a course. Initial work will use the [http://moodle.cvs.sourceforge.net/*checkout*/moodle/moodle/lang/en_utf8/help/enrol/imsenterprise/formatoverview.html IMS Enterprise XML] standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Courses ==&lt;br /&gt;
These functions will allow for definition and management of courses. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Grades ==&lt;br /&gt;
These functions will allow for information about grades to be exchanged. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The code is available in the contrib/patches/ws directory of cvs.&lt;br /&gt;
 cvs -z3 -d:pserver:anonymous@moodle.cvs.sourceforge.net:/cvsroot/moodle co contrib/patches/ws&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=44079 Web Services in forthcoming Moodle] and [http://moodle.org/mod/forum/discuss.php?d=51752 Proposal: Web Services / API / Refactoring Opportunity] forum discussions&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3488</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3488"/>
		<updated>2007-02-02T11:44:46Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Please also see the discussion about a simple to use xml-rpc based api at [[Simple web services|simple web services]].&lt;br /&gt;
&lt;br /&gt;
Provide Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
Specifically,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
The API will be built in two tiers:&lt;br /&gt;
# Generic Moodle web services - This will define the API available to be used by specific protocols.&lt;br /&gt;
# Protocol-specific web services - Using the generic services as the parent, define specific interfaces for SOAP, XML-RPC and others.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Class Structure:&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Base class &amp;lt;code&amp;gt;server.class.php&amp;lt;/code&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
:This class provides most of the necessary functions to authenticate and communicate with Moodle. It also defines the methods that must be implemented by the protocol class. Any web service protocol class must:&lt;br /&gt;
:* call the base class constructor (&amp;lt;code&amp;gt;parent::init()&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the main method (&amp;lt;code&amp;gt;main([$httpdata])&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the request method (&amp;lt;code&amp;gt;request($input)&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the reply method (&amp;lt;code&amp;gt;response($request)&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Derived class (e.g. &amp;lt;code&amp;gt;soapserver.class.php&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The driver for the whole operation is through &amp;lt;code&amp;gt;service.php&amp;lt;/code&amp;gt;. It takes one argument, &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt;, which contains a string identifying the protocol to use (e.g. &#039;soap&#039;).&lt;br /&gt;
&lt;br /&gt;
So, to call a SOAP based web service:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;http://your.server.org/moodle/ws/server.php?type=soap&amp;lt;/code&amp;gt;&lt;br /&gt;
The data for the entire operation comes through the POST mechanism.&lt;br /&gt;
&lt;br /&gt;
== Manage User Data ==&lt;br /&gt;
These functions will allow the exchange of user data, and account management functions. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Course Enrolments ==&lt;br /&gt;
These functions will allow for student, teacher and group management functions within a course. Initial work will use the [http://moodle.cvs.sourceforge.net/*checkout*/moodle/moodle/lang/en_utf8/help/enrol/imsenterprise/formatoverview.html IMS Enterprise XML] standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Courses ==&lt;br /&gt;
These functions will allow for definition and management of courses. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Grades ==&lt;br /&gt;
These functions will allow for information about grades to be exchanged. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The code is available in the contrib/patches/ws directory of cvs.&lt;br /&gt;
 cvs -z3 -d:pserver:anonymous@moodle.cvs.sourceforge.net:/cvsroot/moodle/contrib/patches/ws co contrib/patches/ws&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=44079 Web Services in forthcoming Moodle] and [http://moodle.org/mod/forum/discuss.php?d=51752 Proposal: Web Services / API / Refactoring Opportunity] forum discussions&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3487</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3487"/>
		<updated>2007-02-02T11:27:06Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Code */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Please also see the discussion about a simple to use xml-rpc based api at [[Simple web services|simple web services]].&lt;br /&gt;
&lt;br /&gt;
Provide Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
Specifically,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
The API will be built in two tiers:&lt;br /&gt;
# Generic Moodle web services - This will define the API available to be used by specific protocols.&lt;br /&gt;
# Protocol-specific web services - Using the generic services as the parent, define specific interfaces for SOAP, XML-RPC and others.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Class Structure:&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Base class &amp;lt;code&amp;gt;server.class.php&amp;lt;/code&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
:This class provides most of the necessary functions to authenticate and communicate with Moodle. It also defines the methods that must be implemented by the protocol class. Any web service protocol class must:&lt;br /&gt;
:* call the base class constructor (&amp;lt;code&amp;gt;parent::init()&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the main method (&amp;lt;code&amp;gt;main([$httpdata])&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the request method (&amp;lt;code&amp;gt;request($input)&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the reply method (&amp;lt;code&amp;gt;response($request)&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Derived class (e.g. &amp;lt;code&amp;gt;soapserver.class.php&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The driver for the whole operation is through &amp;lt;code&amp;gt;service.php&amp;lt;/code&amp;gt;. It takes one argument, &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt;, which contains a string identifying the protocol to use (e.g. &#039;soap&#039;).&lt;br /&gt;
&lt;br /&gt;
So, to call a SOAP based web service:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;http://your.server.org/moodle/ws/server.php?type=soap&amp;lt;/code&amp;gt;&lt;br /&gt;
The data for the entire operation comes through the POST mechanism.&lt;br /&gt;
&lt;br /&gt;
== Manage User Data ==&lt;br /&gt;
These functions will allow the exchange of user data, and account management functions. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Course Enrolments ==&lt;br /&gt;
These functions will allow for student, teacher and group management functions within a course. Initial work will use the [http://moodle.cvs.sourceforge.net/*checkout*/moodle/moodle/lang/en_utf8/help/enrol/imsenterprise/formatoverview.html IMS Enterprise XML] standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Courses ==&lt;br /&gt;
These functions will allow for definition and management of courses. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Grades ==&lt;br /&gt;
These functions will allow for information about grades to be exchanged. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The code is available in the contrib/patches/ws directory of cvs.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=44079 Web Services in forthcoming Moodle] and [http://moodle.org/mod/forum/discuss.php?d=51752 Proposal: Web Services / API / Refactoring Opportunity] forum discussions&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3486</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3486"/>
		<updated>2006-11-15T15:16:55Z</updated>

		<summary type="html">&lt;p&gt;Brianking: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Please also see the discussion about a simple to use xml-rpc based api at [[Simple web services|simple web services]].&lt;br /&gt;
&lt;br /&gt;
Provide Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
Specifically,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
The API will be built in two tiers:&lt;br /&gt;
# Generic Moodle web services - This will define the API available to be used by specific protocols.&lt;br /&gt;
# Protocol-specific web services - Using the generic services as the parent, define specific interfaces for SOAP, XML-RPC and others.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Class Structure:&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Base class &amp;lt;code&amp;gt;server.class.php&amp;lt;/code&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
:This class provides most of the necessary functions to authenticate and communicate with Moodle. It also defines the methods that must be implemented by the protocol class. Any web service protocol class must:&lt;br /&gt;
:* call the base class constructor (&amp;lt;code&amp;gt;parent::init()&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the main method (&amp;lt;code&amp;gt;main([$httpdata])&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the request method (&amp;lt;code&amp;gt;request($input)&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the reply method (&amp;lt;code&amp;gt;response($request)&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Derived class (e.g. &amp;lt;code&amp;gt;soapserver.class.php&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The driver for the whole operation is through &amp;lt;code&amp;gt;service.php&amp;lt;/code&amp;gt;. It takes one argument, &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt;, which contains a string identifying the protocol to use (e.g. &#039;soap&#039;).&lt;br /&gt;
&lt;br /&gt;
So, to call a SOAP based web service:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;http://your.server.org/moodle/ws/server.php?type=soap&amp;lt;/code&amp;gt;&lt;br /&gt;
The data for the entire operation comes through the POST mechanism.&lt;br /&gt;
&lt;br /&gt;
== Manage User Data ==&lt;br /&gt;
These functions will allow the exchange of user data, and account management functions. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Course Enrolments ==&lt;br /&gt;
These functions will allow for student, teacher and group management functions within a course. Initial work will use the [http://moodle.cvs.sourceforge.net/*checkout*/moodle/moodle/lang/en_utf8/help/enrol/imsenterprise/formatoverview.html IMS Enterprise XML] standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Courses ==&lt;br /&gt;
These functions will allow for definition and management of courses. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Grades ==&lt;br /&gt;
These functions will allow for information about grades to be exchanged. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Code ==&lt;br /&gt;
The code is available in the contrib/ws directory of cvs.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=44079 Web Services in forthcoming Moodle] and [http://moodle.org/mod/forum/discuss.php?d=51752 Proposal: Web Services / API / Refactoring Opportunity] forum discussions&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3485</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3485"/>
		<updated>2006-11-15T15:15:59Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Please also see the discussion about a simple to use xml-rpc based api at [[Simple web services|simple web services]].&lt;br /&gt;
&lt;br /&gt;
Provide Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
Specifically,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
The API will be built in two tiers:&lt;br /&gt;
# Generic Moodle web services - This will define the API available to be used by specific protocols.&lt;br /&gt;
# Protocol-specific web services - Using the generic services as the parent, define specific interfaces for SOAP, XML-RPC and others.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Class Structure:&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Base class &amp;lt;code&amp;gt;server.class.php&amp;lt;/code&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
:This class provides most of the necessary functions to authenticate and communicate with Moodle. It also defines the methods that must be implemented by the protocol class. Any web service protocol class must:&lt;br /&gt;
:* call the base class constructor (&amp;lt;code&amp;gt;parent::init()&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the main method (&amp;lt;code&amp;gt;main([$httpdata])&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the request method (&amp;lt;code&amp;gt;request($input)&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the reply method (&amp;lt;code&amp;gt;response($request)&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Derived class (e.g. &amp;lt;code&amp;gt;soapserver.class.php&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The driver for the whole operation is through &amp;lt;code&amp;gt;service.php&amp;lt;/code&amp;gt;. It takes one argument, &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt;, which contains a string identifying the protocol to use (e.g. &#039;soap&#039;).&lt;br /&gt;
&lt;br /&gt;
So, to call a SOAP based web service:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;http://your.server.org/moodle/ws/server.php?type=soap&amp;lt;/code&amp;gt;&lt;br /&gt;
The data for the entire operation comes through the POST mechanism.&lt;br /&gt;
&lt;br /&gt;
== Manage User Data ==&lt;br /&gt;
These functions will allow the exchange of user data, and account management functions. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Course Enrolments ==&lt;br /&gt;
These functions will allow for student, teacher and group management functions within a course. Initial work will use the [http://moodle.cvs.sourceforge.net/*checkout*/moodle/moodle/lang/en_utf8/help/enrol/imsenterprise/formatoverview.html IMS Enterprise XML] standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Courses ==&lt;br /&gt;
These functions will allow for definition and management of courses. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Grades ==&lt;br /&gt;
These functions will allow for information about grades to be exchanged. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=44079 Web Services in forthcoming Moodle] and [http://moodle.org/mod/forum/discuss.php?d=51752 Proposal: Web Services / API / Refactoring Opportunity] forum discussions&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3484</id>
		<title>Mnet Web services API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Mnet_Web_services_API&amp;diff=3484"/>
		<updated>2006-11-15T15:14:17Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Provide Moodle with a web service interface to allow exchange of data and information with other systems.&lt;br /&gt;
&lt;br /&gt;
The code is available in the contrib/ws directory of cvs.&lt;br /&gt;
Please also see the discussion about a simple to use xml-rpc based api at [[Simple web services|simple web services]].&lt;br /&gt;
&lt;br /&gt;
Specifically,&lt;br /&gt;
# Manage user data - send and retrieve the information,&lt;br /&gt;
# Manage course enrolments - add/remove teachers and students,&lt;br /&gt;
# Course management - create new courses based on templates,&lt;br /&gt;
# Gradebook info - extract grades information from Moodle.&lt;br /&gt;
&lt;br /&gt;
The API will be built in two tiers:&lt;br /&gt;
# Generic Moodle web services - This will define the API available to be used by specific protocols.&lt;br /&gt;
# Protocol-specific web services - Using the generic services as the parent, define specific interfaces for SOAP, XML-RPC and others.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Class Structure:&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Base class &amp;lt;code&amp;gt;server.class.php&amp;lt;/code&amp;gt;:&amp;lt;br&amp;gt;&lt;br /&gt;
:This class provides most of the necessary functions to authenticate and communicate with Moodle. It also defines the methods that must be implemented by the protocol class. Any web service protocol class must:&lt;br /&gt;
:* call the base class constructor (&amp;lt;code&amp;gt;parent::init()&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the main method (&amp;lt;code&amp;gt;main([$httpdata])&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the request method (&amp;lt;code&amp;gt;request($input)&amp;lt;/code&amp;gt;),&lt;br /&gt;
:* implement the reply method (&amp;lt;code&amp;gt;response($request)&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Derived class (e.g. &amp;lt;code&amp;gt;soapserver.class.php&amp;lt;/code&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
The driver for the whole operation is through &amp;lt;code&amp;gt;service.php&amp;lt;/code&amp;gt;. It takes one argument, &amp;lt;code&amp;gt;type&amp;lt;/code&amp;gt;, which contains a string identifying the protocol to use (e.g. &#039;soap&#039;).&lt;br /&gt;
&lt;br /&gt;
So, to call a SOAP based web service:&amp;lt;br&amp;gt;&lt;br /&gt;
&amp;lt;code&amp;gt;http://your.server.org/moodle/ws/server.php?type=soap&amp;lt;/code&amp;gt;&lt;br /&gt;
The data for the entire operation comes through the POST mechanism.&lt;br /&gt;
&lt;br /&gt;
== Manage User Data ==&lt;br /&gt;
These functions will allow the exchange of user data, and account management functions. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Course Enrolments ==&lt;br /&gt;
These functions will allow for student, teacher and group management functions within a course. Initial work will use the [http://moodle.cvs.sourceforge.net/*checkout*/moodle/moodle/lang/en_utf8/help/enrol/imsenterprise/formatoverview.html IMS Enterprise XML] standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Courses ==&lt;br /&gt;
These functions will allow for definition and management of courses. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
== Manage Grades ==&lt;br /&gt;
These functions will allow for information about grades to be exchanged. Initial work will use the IMS Enterprise XML standard for data definition.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=44079 Web Services in forthcoming Moodle] and [http://moodle.org/mod/forum/discuss.php?d=51752 Proposal: Web Services / API / Refactoring Opportunity] forum discussions&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Administrator]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Enrolment_plugins&amp;diff=3581</id>
		<title>Enrolment plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Enrolment_plugins&amp;diff=3581"/>
		<updated>2006-09-12T16:55:47Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Testing paypal using the paypal developer sandbox  (for moodle 1.6) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
==Testing paypal using the paypal developer sandbox==&lt;br /&gt;
(for moodle 1.4 - 1.6)&lt;br /&gt;
If you follow these steps, you can test paypal payments using the paypal developer sandbox instead of the real paypal site.  No money is actually charged to any account.  All other actions are the same as if you were using the real paypal site.&lt;br /&gt;
* create a paypal developer account at https://developer.paypal.com/cgi-bin/devscr?cmd=_home&lt;br /&gt;
* change the address being used to send data to paypal to use the sandbox. in enrol/paypal/enrol.html:&lt;br /&gt;
** change the form post action to be https://www.sandbox.paypal.com/cgi-bin/webscr instead of https://www.paypal.com/cgi-bin/webscr&lt;br /&gt;
* change the address that is used to check the acknowledgment from paypal.  in enrol/paypal/ipn.php, change:&lt;br /&gt;
** $fp = fsockopen (&#039;www.paypal.com&#039;, 80, $errno, $errstr, 30);&lt;br /&gt;
* to&lt;br /&gt;
** $fp = fsockopen (&#039;www.sandbox.paypal.com&#039;, 80, $errno, $errstr, 30);&lt;br /&gt;
* create a couple of user accounts in the paypal sandbox and test enrolling in a course that requires payment (note that you need to be logged into the paypal sandbox while doing this testing)&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[Enrolment plugins|Enrolment plugins (administrator)]]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?id=2981 Enrolment Plugins] forum&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Enrolment]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Enrolment_plugins&amp;diff=3580</id>
		<title>Enrolment plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Enrolment_plugins&amp;diff=3580"/>
		<updated>2006-09-12T15:21:21Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Testing paypal using the paypal developer sandbox  (for moodle 1.6) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
==Testing paypal using the paypal developer sandbox  (for moodle 1.6)==&lt;br /&gt;
&lt;br /&gt;
If you follow these steps, you can test paypal payments using the paypal developer sandbox instead of the real paypal site.  No money is actually charged to any account.  All other actions are the same as if you were using the real paypal site.&lt;br /&gt;
* create a paypal developer account at https://developer.paypal.com/cgi-bin/devscr?cmd=_home&lt;br /&gt;
* change the address being used to send data to paypal to use the sandbox. in enrol/paypal/enrol.html:&lt;br /&gt;
** change the form post action to be https://www.sandbox.paypal.com/cgi-bin/webscr instead of https://www.paypal.com/cgi-bin/webscr&lt;br /&gt;
* change the address that is used to check the acknowledgment from paypal.  in enrol/paypal/ipn.php, change:&lt;br /&gt;
** $fp = fsockopen (&#039;www.paypal.com&#039;, 80, $errno, $errstr, 30);&lt;br /&gt;
* to&lt;br /&gt;
** $fp = fsockopen (&#039;www.sandbox.paypal.com&#039;, 80, $errno, $errstr, 30);&lt;br /&gt;
* create a couple of user accounts in the paypal sandbox and test enrolling in a course that requires payment (note that you need to be logged into the paypal sandbox while doing this testing)&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[Enrolment plugins|Enrolment plugins (administrator)]]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?id=2981 Enrolment Plugins] forum&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Enrolment]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Enrolment_plugins&amp;diff=3579</id>
		<title>Enrolment plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Enrolment_plugins&amp;diff=3579"/>
		<updated>2006-09-12T14:46:37Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Testing paypal using the paypal developer sandbox  (for moodle 1.6) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
==Testing paypal using the paypal developer sandbox  (for moodle 1.6)==&lt;br /&gt;
* create a paypal developer account at https://developer.paypal.com/cgi-bin/devscr?cmd=_home&lt;br /&gt;
* change the address being used to send data to paypal to use the sandbox. in enrol/paypal/enrol.html:&lt;br /&gt;
** change the form post action to be https://www.sandbox.paypal.com/cgi-bin/webscr instead of https://www.paypal.com/cgi-bin/webscr&lt;br /&gt;
* change the address that is used to check the acknowledgment from paypal.  in enrol/paypal/ipn.php, change:&lt;br /&gt;
** $fp = fsockopen (&#039;www.paypal.com&#039;, 80, $errno, $errstr, 30);&lt;br /&gt;
* to&lt;br /&gt;
** $fp = fsockopen (&#039;www.sandbox.paypal.com&#039;, 80, $errno, $errstr, 30);&lt;br /&gt;
* create a couple of user accounts in the paypal sandbox and test enrolling in a course that requires payment (note that you need to be logged into the paypal sandbox while doing this testing)&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[Enrolment plugins|Enrolment plugins (administrator)]]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?id=2981 Enrolment Plugins] forum&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Enrolment]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Enrolment_plugins&amp;diff=3578</id>
		<title>Enrolment plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Enrolment_plugins&amp;diff=3578"/>
		<updated>2006-09-12T14:46:18Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* Testing paypal using the paypal developer sandbox  (for moodle 1.6) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
==Testing paypal using the paypal developer sandbox  (for moodle 1.6)==&lt;br /&gt;
* create a paypal developer account at https://developer.paypal.com/cgi-bin/devscr?cmd=_home&lt;br /&gt;
* change the address being used to send data to paypal to use the sandbox. in enrol/paypal/enrol.html:&lt;br /&gt;
* * change the form post action to be https://www.sandbox.paypal.com/cgi-bin/webscr instead of https://www.paypal.com/cgi-bin/webscr&lt;br /&gt;
* change the address that is used to check the acknowledgment from paypal.  in enrol/paypal/ipn.php, change:&lt;br /&gt;
** $fp = fsockopen (&#039;www.paypal.com&#039;, 80, $errno, $errstr, 30);&lt;br /&gt;
* to&lt;br /&gt;
** $fp = fsockopen (&#039;www.sandbox.paypal.com&#039;, 80, $errno, $errstr, 30);&lt;br /&gt;
* create a couple of user accounts in the paypal sandbox and test enrolling in a course that requires payment (note that you need to be logged into the paypal sandbox while doing this testing)&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[Enrolment plugins|Enrolment plugins (administrator)]]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?id=2981 Enrolment Plugins] forum&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Enrolment]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Enrolment_plugins&amp;diff=3577</id>
		<title>Enrolment plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Enrolment_plugins&amp;diff=3577"/>
		<updated>2006-09-12T14:45:53Z</updated>

		<summary type="html">&lt;p&gt;Brianking: /* See also */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}&lt;br /&gt;
==Testing paypal using the paypal developer sandbox  (for moodle 1.6)==&lt;br /&gt;
* create a paypal developer account at https://developer.paypal.com/cgi-bin/devscr?cmd=_home&lt;br /&gt;
* change the address being used to send data to paypal to use the sandbox. in enrol/paypal/enrol.html:&lt;br /&gt;
* * change the form post action to be https://www.sandbox.paypal.com/cgi-bin/webscr instead of https://www.paypal.com/cgi-bin/webscr&lt;br /&gt;
* change the address that is used to check the acknowledgment from paypal.  in enrol/paypal/ipn.php, change:&lt;br /&gt;
** $fp = fsockopen (&#039;www.paypal.com&#039;, 80, $errno, $errstr, 30);&lt;br /&gt;
to&lt;br /&gt;
** $fp = fsockopen (&#039;www.sandbox.paypal.com&#039;, 80, $errno, $errstr, 30);&lt;br /&gt;
* create a couple of user accounts in the paypal sandbox and test enrolling in a course that requires payment (note that you need to be logged into the paypal sandbox while doing this testing)&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[Enrolment plugins|Enrolment plugins (administrator)]]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?id=2981 Enrolment Plugins] forum&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Enrolment]]&lt;/div&gt;</summary>
		<author><name>Brianking</name></author>
	</entry>
</feed>