<?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=GregLyon</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=GregLyon"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/Special:Contributions/GregLyon"/>
	<updated>2026-07-18T18:13:11Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Restore_2.0_for_developers&amp;diff=34042</id>
		<title>Restore 2.0 for developers</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Restore_2.0_for_developers&amp;diff=34042"/>
		<updated>2012-05-29T18:28:58Z</updated>

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

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

		<summary type="html">&lt;p&gt;GregLyon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ever since 2006 I&#039;ve been enhancing and supporting a variety of moodle systems.  Most are still based on 1.8 though I&#039;ve worked on a couple 1.9 and am just about to start building some custom modules for a moodle 2.0 (This is spring of 2011).&lt;br /&gt;
&lt;br /&gt;
I am a lead developer at Blue Earth Interactive [http://www.blueearth.net/] located in St. Paul, Minnesota, USA.&lt;/div&gt;</summary>
		<author><name>GregLyon</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Greg_Lyon&amp;diff=19857</id>
		<title>User:Greg Lyon</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Greg_Lyon&amp;diff=19857"/>
		<updated>2011-05-12T14:02:51Z</updated>

		<summary type="html">&lt;p&gt;GregLyon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Ever since 2006 I&#039;ve been enhancing and supporting a variety of moodle systems.  Most are still based on 1.8 though I&#039;ve worked on a couple 1.9 and am just about to start building some custom modules for a moodle 2.0 (This is spring of 2011).&lt;br /&gt;
&lt;br /&gt;
I am a lead developer at Blue Earth Interactive, [http://www.blueearth.net/].&lt;/div&gt;</summary>
		<author><name>GregLyon</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=XMLDB_defining_an_XML_structure&amp;diff=4761</id>
		<title>XMLDB defining an XML structure</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=XMLDB_defining_an_XML_structure&amp;diff=4761"/>
		<updated>2007-04-16T17:11:43Z</updated>

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

		<summary type="html">&lt;p&gt;GregLyon: /* Getting started */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Quiz developer docs}}&lt;br /&gt;
To follow this guide, you need to get hold of the qtype_NEW plugin template. You can get it from CVS in the contrib area or from the [http://moodle.org/mod/data/view.php?id=6009 modules and plugins database]. The rest of this guide is just a copy of the README.txt file from in there. (Or is the readme file a copy of this page?)&lt;br /&gt;
&lt;br /&gt;
==New question type template README file==&lt;br /&gt;
&lt;br /&gt;
Welcome to the new question type template.&lt;br /&gt;
&lt;br /&gt;
This blank skeleton is a good place to start if you want to implement your own Moodle question type plugin.&lt;br /&gt;
&lt;br /&gt;
Another good way to start is by looking at an existing question type that has good documentation in the code like TODO (if there is one, otherwise we&#039;ll hope that Tims will &amp;quot;light the way&amp;quot; :-) )&lt;br /&gt;
&lt;br /&gt;
The latest version of the template can be found in CVS in http://moodle.cvs.sourceforge.net/moodle/contrib/plugins/question/type/TEMPLATE/ (was in contrib/qtype_NEW module). Because you want your own copy do a cvs export, not a cvs checkout. The package can also been downloaded from [http://moodle.org/mod/data/view.php?d=13&amp;amp;rid=443 the Modules and plugins database].&lt;br /&gt;
&lt;br /&gt;
The latest version of this help can be read (nicely formatted) at  https://docs.moodle.org/en/How_to_write_a_question_type_plugin&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING, THIS TEMPLATE IS NOT COMPLETE YET!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Getting started==&lt;br /&gt;
&lt;br /&gt;
Before you get to the interesting bit, you need to do a bit of file-renaming and search-and-replacing to turn this generic template into your own question type. You need to have chosen two things &lt;br /&gt;
&lt;br /&gt;
;The identifier for your question type&lt;br /&gt;
:This is a string of lowercase letters and perhaps underscores that the Moodle code  uses to refer to your question type. This needs to be unique, so perhaps start it with your initials. For example, all the quetions types I create while working at the OU will be referred to as ou_something. For the rest of these instructions, I will assume you have chosen &#039;myqtypeidentifier&#039;.&lt;br /&gt;
;The name of your question type&lt;br /&gt;
:This is the name that people will see in the Moodle User-interface (in the English translation). For these instructions I will assume you have chosen  &#039;My Question Type Name&#039;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;strong&amp;gt;NOTE:&amp;lt;/strong&amp;gt; depending on where you got the template, some of the files and folders may be called TEMPLATE instead of QTYPEID.  The contents of the files will still contain the text QTYPEID in either case.&lt;br /&gt;
&lt;br /&gt;
Then you need to&lt;br /&gt;
&lt;br /&gt;
# Rename the directory question/type/QTYPEID to question/type/myqtypeidentifier.&lt;br /&gt;
# Rename the language file lang/en_utf8/qtype_QTYPEID.html to  lang/en_utf8/qtype_myqtypeidentifier.html&lt;br /&gt;
# Rename the help file lang/en_utf8/help/quiz/QTYPEID.html to  lang/en_utf8/help/quiz/myqtypeidentifier.html&lt;br /&gt;
# Search and replace &#039;QTYPEID&#039; with &#039;myqtypeidentifier&#039; in all the files. You should find:&lt;br /&gt;
#* 2 occurrences in the language file&lt;br /&gt;
#* 1 occurrence in db/install.xml&lt;br /&gt;
#* 5 occurrences in simpletest/testquestiontype.php&lt;br /&gt;
#* 3 occurrences in editquestion.html&lt;br /&gt;
#* 1 occurrence in editquestion.php&lt;br /&gt;
#* 3 occurrences in questiontype.php&lt;br /&gt;
# Search and replace &#039;QTYPENAME&#039; with &#039;My Question Type Name&#039; in all the files. You should find:&lt;br /&gt;
#* 1 occurrence in the help file&lt;br /&gt;
#* 3 occurrences in the language file&lt;br /&gt;
#* 2 occurrences in questiontype.php&lt;br /&gt;
# Search and replace YOURNAME with your name. (This is only in the comments at  the top of each file so it is not critical, but surely you want to take credit for your work.) You should find one occurrence in:&lt;br /&gt;
#* the language file.&lt;br /&gt;
#* simpletest/testquestiontype.php&lt;br /&gt;
#* editquestion.html&lt;br /&gt;
#* editquestion.php&lt;br /&gt;
#* questiontype.php&lt;br /&gt;
# Search and replace YOUREMAILADDRESS with your email address. (Again, this is only in the file header comments, but it is helpful if people can contact you if they have any questions about your code.) You should find:&lt;br /&gt;
#*  There should be one occurrence of YOUREMAILADDRESS in each of the files listed under YOURNAME.&lt;br /&gt;
# Search and replace YOURPACKAGENAME with a package name for your code. This is used by PHPdoc when building the documentation for your classes. I suggest you make up one package name for all the question types you write. For example all the question types I write are in the package ou_questiontypes.&lt;br /&gt;
#* There should be one occurrence of YOURPACKAGENAME in each of the files listed under YOURNAME.&lt;br /&gt;
# Edit icon.gif to make an icon that represents your question type.&lt;br /&gt;
&lt;br /&gt;
Finally, so you can test your code while working on it, you need to copy the lang and question folders on top of the folders in your Moodle test installation. This copy should not ovewrite any existing files. It should just add some new files and folders to that directory tree.&lt;br /&gt;
&lt;br /&gt;
==Now for the interesting bit==&lt;br /&gt;
&lt;br /&gt;
Now you need to write the code to&lt;br /&gt;
&lt;br /&gt;
# Create any database tables you need. This works just like normal in Moodle with the files in the db directory and the version.php file. If you don&#039;t need to create any database tables, you can delete the db directory.&lt;br /&gt;
# Create the editing form for you question type, and the code to populate it.  That means adding code to the files editquestion.html and editquestion.php.&lt;br /&gt;
# Create the template that will display the question to the student. This is in display.html. To make good flexible formating you should use CSS classes and use already existing ones when possible. An easy way to find which existing CSS classes exist is TODO&lt;br /&gt;
# Implement the rest of the question type class. This is in questiontype.php. You need to &lt;br /&gt;
## TODO finish writing this section.&lt;br /&gt;
# Make sure that the language file contains all the strings you refer to. Note that some of the strings you want may already be in the qtype_base.php and moodle.php language files in Moodle core. If so, use the existing strings. An easy way to find the existing strings is TODO&lt;br /&gt;
# Write the help file for your question type, and any other help files you need.&lt;br /&gt;
# Write a set of unit tests for you question type.&lt;br /&gt;
&lt;br /&gt;
All the places in the code where you need to do things are marked TODO, and there are comments right there giving more detailed instructions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note that these comments are not complete or accurate yet. I have only just started implementing my own question type, and I will be filling in those comments and finishing this document as I go along.&#039;&#039;--[[User:Tim Hunt|Tim Hunt]] 16:29, 25 August 2006 (CDT)&lt;br /&gt;
&lt;br /&gt;
==When you have finished==&lt;br /&gt;
&lt;br /&gt;
Consider checking your question type into the contrib area of  the Moodle CVS server (if you have access) so that other people can share it.&lt;br /&gt;
&lt;br /&gt;
Add your new question type to the [http://moodle.org/mod/data/view.php?id=6009 modules and plugins database].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Quiz]]&lt;/div&gt;</summary>
		<author><name>GregLyon</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Question_type_plugin_how_to&amp;diff=2561</id>
		<title>Question type plugin how to</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Question_type_plugin_how_to&amp;diff=2561"/>
		<updated>2007-03-29T20:19:51Z</updated>

		<summary type="html">&lt;p&gt;GregLyon: /* New question type template README file */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Quiz developer docs}}&lt;br /&gt;
To follow this guide, you need to get hold of the qtype_NEW plugin template. You can get it from CVS in the contrib area or from the [http://moodle.org/mod/data/view.php?id=6009 modules and plugins database]. The rest of this guide is just a copy of the README.txt file from in there. (Or is the readme file a copy of this page?)&lt;br /&gt;
&lt;br /&gt;
==New question type template README file==&lt;br /&gt;
&lt;br /&gt;
Welcome to the new question type template.&lt;br /&gt;
&lt;br /&gt;
This blank skeleton is a good place to start if you want to implement your own Moodle question type plugin.&lt;br /&gt;
&lt;br /&gt;
Another good way to start is by looking at an existing question type that has good documentation in the code like TODO (if there is one, otherwise we&#039;ll hope that Tims will &amp;quot;light the way&amp;quot; :-) )&lt;br /&gt;
&lt;br /&gt;
The latest version of the template can be found in CVS in http://moodle.cvs.sourceforge.net/moodle/contrib/plugins/question/type/TEMPLATE/ (was in contrib/qtype_NEW module). Because you want your own copy do a cvs export, not a cvs checkout. The package can also been downloaded from [http://moodle.org/mod/data/view.php?d=13&amp;amp;rid=443 the Modules and plugins database].&lt;br /&gt;
&lt;br /&gt;
The latest version of this help can be read (nicely formatted) at  https://docs.moodle.org/en/How_to_write_a_question_type_plugin&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;WARNING, THIS TEMPLATE IS NOT COMPLETE YET!&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
==Getting started==&lt;br /&gt;
&lt;br /&gt;
Before you get to the interesting bit, you need to do a bit of file-renaming and search-and-replacing to turn this generic template into your own question type. You need to have chosen two things&lt;br /&gt;
&lt;br /&gt;
;The identifier for your question type&lt;br /&gt;
:This is a string of lowercase letters and perhaps underscores that the Moodle code  uses to refer to your question type. This needs to be unique, so perhaps start it with your initials. For example, all the quetions types I create while working at the OU will be referred to as ou_something. For the rest of these instructions, I will assume you have chosen &#039;myqtypeidentifier&#039;.&lt;br /&gt;
;The name of your question type&lt;br /&gt;
:This is the name that people will see in the Moodle User-interface (in the English translation). For these instructions I will assume you have chosen  &#039;My Question Type Name&#039;.&lt;br /&gt;
&lt;br /&gt;
Then you need to&lt;br /&gt;
&lt;br /&gt;
# Rename the directory question/type/QTYPEID to question/type/myqtypeidentifier.&lt;br /&gt;
# Rename the language file lang/en_utf8/qtype_QTYPEID.html to  lang/en_utf8/qtype_myqtypeidentifier.html&lt;br /&gt;
# Rename the help file lang/en_utf8/help/quiz/QTYPEID.html to  lang/en_utf8/help/quiz/myqtypeidentifier.html&lt;br /&gt;
# Search and replace &#039;QTYPEID&#039; with &#039;myqtypeidentifier&#039; in all the files. You should find:&lt;br /&gt;
#* 2 occurrences in the language file&lt;br /&gt;
#* 1 occurrence in db/install.xml&lt;br /&gt;
#* 5 occurrences in simpletest/testquestiontype.php&lt;br /&gt;
#* 3 occurrences in editquestion.html&lt;br /&gt;
#* 1 occurrence in editquestion.php&lt;br /&gt;
#* 3 occurrences in questiontype.php&lt;br /&gt;
# Search and replace &#039;QTYPENAME&#039; with &#039;My Question Type Name&#039; in all the files. You should find:&lt;br /&gt;
#* 1 occurrence in the help file&lt;br /&gt;
#* 3 occurrences in the language file&lt;br /&gt;
#* 2 occurrences in questiontype.php&lt;br /&gt;
# Search and replace YOURNAME with your name. (This is only in the comments at  the top of each file so it is not critical, but surely you want to take credit for your work.) You should find one occurrence in:&lt;br /&gt;
#* the language file.&lt;br /&gt;
#* simpletest/testquestiontype.php&lt;br /&gt;
#* editquestion.html&lt;br /&gt;
#* editquestion.php&lt;br /&gt;
#* questiontype.php&lt;br /&gt;
# Search and replace YOUREMAILADDRESS with your email address. (Again, this is only in the file header comments, but it is helpful if people can contact you if they have any questions about your code.) You should find:&lt;br /&gt;
#*  There should be one occurrence of YOUREMAILADDRESS in each of the files listed under YOURNAME.&lt;br /&gt;
# Search and replace YOURPACKAGENAME with a package name for your code. This is used by PHPdoc when building the documentation for your classes. I suggest you make up one package name for all the question types you write. For example all the question types I write are in the package ou_questiontypes.&lt;br /&gt;
#* There should be one occurrence of YOURPACKAGENAME in each of the files listed under YOURNAME.&lt;br /&gt;
# Edit icon.gif to make an icon that represents your question type.&lt;br /&gt;
&lt;br /&gt;
Finally, so you can test your code while working on it, you need to copy the lang and question folders on top of the folders in your Moodle test installation. This copy should not ovewrite any existing files. It should just add some new files and folders to that directory tree.&lt;br /&gt;
&lt;br /&gt;
==Now for the interesting bit==&lt;br /&gt;
&lt;br /&gt;
Now you need to write the code to&lt;br /&gt;
&lt;br /&gt;
# Create any database tables you need. This works just like normal in Moodle with the files in the db directory and the version.php file. If you don&#039;t need to create any database tables, you can delete the db directory.&lt;br /&gt;
# Create the editing form for you question type, and the code to populate it.  That means adding code to the files editquestion.html and editquestion.php.&lt;br /&gt;
# Create the template that will display the question to the student. This is in display.html. To make good flexible formating you should use CSS classes and use already existing ones when possible. An easy way to find which existing CSS classes exist is TODO&lt;br /&gt;
# Implement the rest of the question type class. This is in questiontype.php. You need to &lt;br /&gt;
## TODO finish writing this section.&lt;br /&gt;
# Make sure that the language file contains all the strings you refer to. Note that some of the strings you want may already be in the qtype_base.php and moodle.php language files in Moodle core. If so, use the existing strings. An easy way to find the existing strings is TODO&lt;br /&gt;
# Write the help file for your question type, and any other help files you need.&lt;br /&gt;
# Write a set of unit tests for you question type.&lt;br /&gt;
&lt;br /&gt;
All the places in the code where you need to do things are marked TODO, and there are comments right there giving more detailed instructions.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;Note that these comments are not complete or accurate yet. I have only just started implementing my own question type, and I will be filling in those comments and finishing this document as I go along.&#039;&#039;--[[User:Tim Hunt|Tim Hunt]] 16:29, 25 August 2006 (CDT)&lt;br /&gt;
&lt;br /&gt;
==When you have finished==&lt;br /&gt;
&lt;br /&gt;
Consider checking your question type into the contrib area of  the Moodle CVS server (if you have access) so that other people can share it.&lt;br /&gt;
&lt;br /&gt;
Add your new question type to the [http://moodle.org/mod/data/view.php?id=6009 modules and plugins database].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:Quiz]]&lt;/div&gt;</summary>
		<author><name>GregLyon</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=lib/formslib.php_Usage&amp;diff=5737</id>
		<title>lib/formslib.php Usage</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=lib/formslib.php_Usage&amp;diff=5737"/>
		<updated>2007-03-09T19:59:02Z</updated>

		<summary type="html">&lt;p&gt;GregLyon: /* Basic Usage in A Normal Page */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Formslib}}&lt;br /&gt;
==Usage of Formslib==&lt;br /&gt;
&lt;br /&gt;
There are many phpdoc style comments in lib/formslib.php&lt;br /&gt;
&lt;br /&gt;
course/edit.php and the included course/edit_form.php provide a good example of usage of this library.&lt;br /&gt;
&lt;br /&gt;
Also see the PEAR docs for [http://pear.php.net/package/HTML_QuickForm/ HTML_QuickForm docs] I found this [http://pear.php.net/manual/en/package.html.html-quickform.tutorial.php quick tutorial] and this [http://www.midnighthax.com/quickform.php slightly longer one] particularly useful.&lt;br /&gt;
&lt;br /&gt;
We created some special wrapper functions for moodle. $mform-&amp;gt;get_data() returns null if no data has been submitted or validation fails or returns an object with the contents of the submitted data.&lt;br /&gt;
&lt;br /&gt;
===Basic Usage in A Normal Page===&lt;br /&gt;
&lt;br /&gt;
Generally the structure of a page with a form on it looks like this :&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
require_once(&#039;pathtoformdesctiption&#039;);&lt;br /&gt;
//you&#039;ll process some page parameters at the top here and get the info about&lt;br /&gt;
//what instance of your module and what course you&#039;re in etc. Make sure you&lt;br /&gt;
//include hidden variable in your forms which have their defaults set in set_data&lt;br /&gt;
//which pass these variables from page to page&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
$mform = new yourmod_formfunction_form();//name of the form you defined in file above.&lt;br /&gt;
//default &#039;action&#039; for form is strip_querystring(qualified_me())&lt;br /&gt;
if ($mform-&amp;gt;is_cancelled()){&lt;br /&gt;
    //you need this section if you have a cancel button on your form&lt;br /&gt;
    //here you tell php what to do if your user presses cancel&lt;br /&gt;
    //probably a redirect is called for!&lt;br /&gt;
} else if ($fromform=$mform-&amp;gt;get_data()){&lt;br /&gt;
//this branch is where you process validated data.&lt;br /&gt;
&lt;br /&gt;
} else {&lt;br /&gt;
// this branch is executed if the form is submitted but the data doesn&#039;t validate and the form should be redisplayed&lt;br /&gt;
// or on the first display of the form.&lt;br /&gt;
    //setup strings for heading&lt;br /&gt;
    print_header_simple($streditinga, &#039;&#039;,&lt;br /&gt;
     &amp;quot;&amp;lt;a href=\&amp;quot;$CFG-&amp;gt;wwwroot/mod/$module-&amp;gt;name/index.php?id=$course-&amp;gt;id\&amp;quot;&amp;gt;$strmodulenameplural&amp;lt;/a&amp;gt; -&amp;gt;&lt;br /&gt;
     $strnav $streditinga&amp;quot;, $mform-&amp;gt;focus(), &amp;quot;&amp;quot;, false);&lt;br /&gt;
    //notice use of $mform-&amp;gt;focus() above which puts the cursor &lt;br /&gt;
    //in the first form field or the first field with an error.&lt;br /&gt;
&lt;br /&gt;
    //call to print_heading_with_help or print_heading? then :&lt;br /&gt;
    &lt;br /&gt;
    //put data you want to fill out in the form into array $toform here then :&lt;br /&gt;
&lt;br /&gt;
    $mform-&amp;gt;set_data($toform);&lt;br /&gt;
    $mform-&amp;gt;display();&lt;br /&gt;
    print_footer($course);&lt;br /&gt;
&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Defining Your Form Class===&lt;br /&gt;
&lt;br /&gt;
The form class tells us about the structure of the form. It is encouraged for you to put this in file called {function}_form.php probably in the same folder in which the page that uses it is located. The name of your class should be {modname}_{funciton}_form eg. forum_post_form or course_edit_form. These classes will extend class moodleform.&lt;br /&gt;
&lt;br /&gt;
Note the name you give the class is used as the id attrbute of your form in html (any trailing &#039;_form&#039; is chopped off&#039;). Your form class name should be unique in order for it to selectable in CSS by theme designers who may want to tweak the css just for that form.&lt;br /&gt;
&lt;br /&gt;
====definition()====&lt;br /&gt;
&lt;br /&gt;
[[lib/formslib.php_Form_Definition|Help is here for defining your form]] by defining a function definition() in your form class that sets up your form structure.&lt;br /&gt;
&lt;br /&gt;
===Use in Activity Modules Add / Update Forms===&lt;br /&gt;
&lt;br /&gt;
Syntax is the same in activity modules to create your update / add page. We are still supporting mod.html but if you want to use the new formslib then you can in Moodle 1.8 just include in your module directory the file mod_form.php instead of mod.html and it will be automatically detected. Inside this file you define a class with class name &#039;{modname}__mod_form&#039; which extends the class &#039;moodleform_mod&#039;. See many examples of the core modules which have been converted to use formslib already.&lt;br /&gt;
&lt;br /&gt;
====defaults_preprocessing====&lt;br /&gt;
&lt;br /&gt;
For activity modules you use the same syntax to define your form. You may also want to override method defaults_preprocessing this can be used to take the data from the data base and tell the form how to fill out defaults in the form with this data. For example in the forum module in forum/mod_form.php we needed to tick an enable check box if a date had been selected in the date select. Normally data is loaded from the database and directly loaded into form fields with the same name as the database record you only need to use defaults_preprocessing if there isn&#039;t this one to one relationship. Another example is the lesson/mod_form.php which takes the data from the database and unserializes it. choice/mod_form.php shows an exampe of loading data from another db table referred to by a foreign key, to include in the form.&lt;br /&gt;
&lt;br /&gt;
====Post Processing Still Done in lib.php Functions====&lt;br /&gt;
&lt;br /&gt;
Post processing of data is done in lib.php functions modname_add_instance and modname_update_instance after the data has been validated. When migrating a from little change needs to be made in the post processing often. An exception is that date and date_time_selector fields automatically have their submitted data turned into timestamps so you don&#039;t need to do this in your add and update functions anymore.&lt;br /&gt;
&lt;br /&gt;
====Automatically Including Standard Activity Module Form Elements====&lt;br /&gt;
&lt;br /&gt;
Standard activity module form elements are automatically included using the moodleform_mod method standard_coursemodule_elements(). The default is to include a visibility and groupsmode select box and to include all necessary hidden fields. You can pass a parameter false to tell the method that your module doesn&#039;t support groups and so you don&#039;t want the groupsmode select button.&lt;br /&gt;
&lt;br /&gt;
[[Category:Formslib]]&lt;/div&gt;</summary>
		<author><name>GregLyon</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=User:Greg_Lyon&amp;diff=19856</id>
		<title>User:Greg Lyon</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=User:Greg_Lyon&amp;diff=19856"/>
		<updated>2006-06-29T19:01:12Z</updated>

		<summary type="html">&lt;p&gt;GregLyon: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Hello!  Currently (June &#039;06) evaluating Moodle 1.6.&lt;/div&gt;</summary>
		<author><name>GregLyon</name></author>
	</entry>
</feed>