<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://docs.moodle.org/test/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Nicolasconnault</id>
	<title>MoodleDocs - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://docs.moodle.org/test/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Nicolasconnault"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/Special:Contributions/Nicolasconnault"/>
	<updated>2026-10-01T01:18:13Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Grades_FAQ&amp;diff=47582</id>
		<title>Grades FAQ</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Grades_FAQ&amp;diff=47582"/>
		<updated>2008-12-03T07:15:02Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Grades}}&lt;br /&gt;
== General ==&lt;br /&gt;
&lt;br /&gt;
===How can I change how grades are displayed?===&lt;br /&gt;
&lt;br /&gt;
Grades may be displayed as as actual grades, as percentages (in reference to the minimum and maximum grades) or as letters.&lt;br /&gt;
&lt;br /&gt;
The default grade display type for the site is set by an administrator in &#039;&#039;Administration &amp;gt; Grades &amp;gt; [[Grade item settings]]&#039;&#039;. However, this may be changed at course level.&lt;br /&gt;
&lt;br /&gt;
To change how grades are displayed for particular [[Grade items|grade items]], or category and course summaries (called aggregations):&lt;br /&gt;
&lt;br /&gt;
# Follow the grades link in the course administration block.&lt;br /&gt;
# Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# Click the edit icon for the grade item, category total or course total.&lt;br /&gt;
# From the Grade display type menu, select real (for actual grades), percentage or letter.&lt;br /&gt;
# Click the &amp;quot;Save changes&amp;quot; button at the bottom of the page.&lt;br /&gt;
&lt;br /&gt;
Alternatively, to change how grades are displayed for the whole course:&lt;br /&gt;
&lt;br /&gt;
# Follow the grades link in the course administration block.&lt;br /&gt;
# Select &amp;quot;Course settings&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# From the Grade display type menu, select real (for actual grades), percentage or letter.&lt;br /&gt;
# Click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
===How can I hide entered grades until a specified date?===&lt;br /&gt;
&lt;br /&gt;
To set a &amp;quot;Hidden until&amp;quot; date:&lt;br /&gt;
&lt;br /&gt;
#Access the course gradebook via the grades link in the course administration block.&lt;br /&gt;
#Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
#Click on the edit icon opposite the activity for which a &amp;quot;Hidden until&amp;quot; date is to be set.&lt;br /&gt;
#On the edit grade item page, ensure that advanced settings are displayed. (Click the &amp;quot;Show advanced&amp;quot; button if not.)&lt;br /&gt;
#Enable the &amp;quot;Hidden until&amp;quot; setting by unchecking the disable checkbox, then set a date.&lt;br /&gt;
#Click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
=== Is it possible to show the teachers/administrators&#039; grades in the grader report? ===&lt;br /&gt;
Yes, at the site level you can define which roles will appear in the grader report. This can be found in [[General_grade_settings#Graded_Roles|Administration &amp;gt; Grades &amp;gt; General settings]]. Also read [http://moodle.org/mod/forum/discuss.php?d=92612 this discussion] for some more ideas.&lt;br /&gt;
&lt;br /&gt;
===Why can&#039;t I change a grade within an assignment after changing it in the gradebook?===&lt;br /&gt;
&lt;br /&gt;
When you edit a grade directly in the gradebook, an &amp;quot;overridden&amp;quot; flag is set, meaning that the grade can no longer be changed from within the assignment.&lt;br /&gt;
&lt;br /&gt;
However, the flag can be removed by turning editing on in the [[Grader report|grader report]], then clicking the [[Grade editing|edit grade]] icon, unchecking the overridden box and saving the changes.&lt;br /&gt;
&lt;br /&gt;
===How do I get groups to show up in the grader report?===&lt;br /&gt;
&lt;br /&gt;
For groups to show up in the grader report, group mode should be set to visible or separate groups in the [[Course settings|course settings]]. This will result in a groups dropdown menu being displayed, enabling a teacher to view the grades of all participants, or only the grades for a selected group.&lt;br /&gt;
&lt;br /&gt;
== Reports ==&lt;br /&gt;
=== How do I create my own custom gradebook reports? ===&lt;br /&gt;
Here is a [[Development:Gradebook_Report_Tutorial|tutorial]] explaining all the main steps involved.&lt;br /&gt;
&lt;br /&gt;
== Aggregation ==&lt;br /&gt;
=== I can&#039;t find where to change the aggregation type for my gradebook categories! ===&lt;br /&gt;
Each category has an aggregation type, which can be changed through that category&#039;s &amp;quot;edit&amp;quot; page. To access that page, you must use one of 2 ways:&lt;br /&gt;
&lt;br /&gt;
1. In the grader report, turn &amp;quot;Editing&amp;quot; on, then click the little &amp;quot;hand&amp;quot; icon next to the category whose aggregation you want to change&lt;br /&gt;
2. In the &amp;quot;Edit categories and Items&amp;quot; page (accessible through the &amp;quot;choose an action&amp;quot; menu, top left), you see a tree view of the categories and items in your gradebook. The top category is the course category. Each category also has a &amp;quot;hand&amp;quot; icon, which leads to the category edit page&lt;br /&gt;
&lt;br /&gt;
=== How can I grade some of my activities without the results affecting my students&#039; course total? ===&lt;br /&gt;
#Create two [[Grade categories]], one for your &amp;quot;activities still being graded,&amp;quot; and one for your &amp;quot;released&amp;quot; activities.&lt;br /&gt;
#Ensure that &amp;quot;Aggregate including subcategories&amp;quot; (an advanced option) is unchecked for your top level course grade category.&lt;br /&gt;
##Where is this?  In gradebook (grader report), in the upper right corner, click the &amp;quot;Turn Editing On&amp;quot; button.&lt;br /&gt;
##Click the edit icon next to the &amp;quot;course category&amp;quot; (usually your course name, just above the quiz names and below all the clickable links that were revealed when you turned editing on)&lt;br /&gt;
##Then make sure you have the &amp;quot;Show Advanced&amp;quot; option turned on.&lt;br /&gt;
#Edit the &amp;quot;activities still being graded&amp;quot; category&#039;s &amp;quot;course total&amp;quot; item. (This is one of the categories you created above.)&lt;br /&gt;
##Where is this?  Look for the edit icon under &amp;quot;category total&amp;quot; that is below this category&#039;s name&lt;br /&gt;
#Set the &amp;quot;grade type&amp;quot; to &amp;quot;none&amp;quot;.&lt;br /&gt;
#Tick the &amp;quot;Hidden&amp;quot; checkbox.&lt;br /&gt;
#Save your changes.&lt;br /&gt;
#Move all your activities being graded in the &amp;quot;activities still being graded&amp;quot;  category.&lt;br /&gt;
#Move all your activities already graded in the &amp;quot;released&amp;quot; category.&lt;br /&gt;
&lt;br /&gt;
Note: I rewrote this a bit, to help people find where things are.  However, this method didn&#039;t seem to work for me on Moodle 1.9.&lt;br /&gt;
&lt;br /&gt;
=== My student completed only one activity out of 5, but his course total shows 100%. How do I show a more &amp;quot;progressive&amp;quot; course total? ===&lt;br /&gt;
By default, only non-empty grades are aggregated, the others are ignored. However, you can change this setting as well as others that affect the course total, by turning &amp;quot;Editing&amp;quot; on in the grader report, and clicking the &amp;quot;Edit&amp;quot; icon next to the course category (the very top row of the grader report).&lt;br /&gt;
&lt;br /&gt;
You can untick the box &amp;quot;Aggregate only non-empty grades&amp;quot; if you want to show a more &amp;quot;progressive&amp;quot; score for each student. Their empty grades will count as a 0 and will be counted in the course mean/total.&lt;br /&gt;
 &lt;br /&gt;
If you prefer to show a sum of points, rather than a percentage, you can change the course category&#039;s aggregation method to &amp;quot;Sum of grades&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== How can I display the average grade for my course categories (not grade categories)? ===&lt;br /&gt;
In Moodle 1.9 there is no way to aggregate course totals within each category. The gradebook is course-centered, and there is currently no User Interface for showing grades within an entire course category at once.&lt;br /&gt;
&lt;br /&gt;
=== How can I setup weighted assignments? ===&lt;br /&gt;
See [[Using &amp;quot;Weighted Mean of Grades&amp;quot; to weight categories containing assignments]].&lt;br /&gt;
&lt;br /&gt;
== Categories ==&lt;br /&gt;
=== How many depths of categories/subcategories can I create? ===&lt;br /&gt;
There is no programmatic limit, but there are practical limits. Very deeply nested structures are difficult to manage. 3 levels of categories should be sufficient for most situations. Note that there is always at least one level of categories, since the Course category always encompasses all other categories and grade items, can cannot be deleted.&lt;br /&gt;
&lt;br /&gt;
=== I can&#039;t find setting X in the grade category edit page! Where is it? ===&lt;br /&gt;
If a setting documented on the [[Grade categories]] page does not appear on your edit page, it may mean that it is set globally in your site. See [[Grade_category_settings#Forcing_settings|Forcing settings]] for more information.&lt;br /&gt;
&lt;br /&gt;
== Outcomes ==&lt;br /&gt;
=== I have just upgraded to Moodle 1.9, and I want to set up an outcome item for my course. What are the steps required? ===&lt;br /&gt;
# Administration &amp;gt; Grades &amp;gt; General settings &amp;gt; [[General_grade_settings#Enable_outcomes|Enable outcomes]]&lt;br /&gt;
#[[Scales#Creating_a_new_scale|Create a scale]]&lt;br /&gt;
#Create a course outcome (read the [[Outcomes| outcomes documentation]] for instructions). Assign to it the scale you just created.&lt;br /&gt;
#Assign the outcome to your course&lt;br /&gt;
#Enter the &amp;quot;Grades&amp;quot; section of your course, from the course administration block&lt;br /&gt;
#In the Actions menu (top left), select Edit -&amp;gt; Categories and Items&lt;br /&gt;
#Click &amp;quot;Add outcome item&amp;quot;&lt;br /&gt;
#Follow the instructions of the [[Outcome items|outcome items documentation]] to create the outcome item&lt;br /&gt;
&lt;br /&gt;
You can now give your students a rating on the outcome dimension you just created. If you created a standard outcome, you will be able to use it in other courses and follow your students&#039; performance across these courses.&lt;br /&gt;
&lt;br /&gt;
===How can I remove an outcome from an activity?===&lt;br /&gt;
&lt;br /&gt;
An outcome can be removed from an activity by deleting it on the gradebook edit categories and items page. This results in the outcomes being deselected on the update activity page.&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
=== The activity module (Module name) doesn&#039;t support grading. How can I give my students a grade anyway? ===&lt;br /&gt;
You can create a [[Grade_items#Manual_grade_items|grade item]] manually in the gradebook. You will have to grade your students through the [[Grader report]] interface (in editing mode).&lt;br /&gt;
&lt;br /&gt;
=== I just graded some of my students using the (Module name) interface, but the results aren&#039;t showing up in the grader report. What&#039;s going on? ===&lt;br /&gt;
Here are some of the possible reasons:&lt;br /&gt;
&lt;br /&gt;
#The corresponding [[Grade items|grade item]] is [[Grade_locking#In_grade_items|locked]], or its parent [[Grade categories|category]] is [[Grade_locking#In_grade_categories|locked]].&lt;br /&gt;
#The module code is not using the [[Development:Grades#API_for_communication_with_modules.2Fblocks|gradebook API]] correctly&lt;br /&gt;
&lt;br /&gt;
=== I just created a new assignment with the &amp;quot;Grade&amp;quot; setting set to &amp;quot;No grade&amp;quot;, but it still appears in the gradebook ===&lt;br /&gt;
The reason is that the gradebook is now the place where both numerical and textual types of feedback are recorded for all activity modules. The word &amp;quot;Grading&amp;quot; in assignment relates only to numerical grades, but the ability to give text feedback remains, and must be recorded in the gradebook. This is why a grade item is created for it. You can hide the grade item if you do not want it to appear in the user reports.&lt;br /&gt;
&lt;br /&gt;
==Weights and extra credits==&lt;br /&gt;
&lt;br /&gt;
===How do I create an assignment for which students can receive a grade higher than the maximum?===&lt;br /&gt;
---Documentation yet to be written---&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[Gradebook 1.9 Tutorial]]&lt;br /&gt;
*Gradebook Scenarios/Use Cases [https://docs.moodle.org/en/experimental:_gb_tutoring]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?id=2122 Gradebook forum]&lt;br /&gt;
&lt;br /&gt;
Using Moodle forum discussions:&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=102609 Can I aggregate only non hidden items?]&lt;br /&gt;
&lt;br /&gt;
[[Category:FAQ]]&lt;br /&gt;
&lt;br /&gt;
[[ca:PMF de les qualificacions]]&lt;br /&gt;
[[fr:FAQ des notes]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Repository_plugins&amp;diff=46689</id>
		<title>Development:Repository plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Repository_plugins&amp;diff=46689"/>
		<updated>2008-11-13T12:05:57Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Administration APIs */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;A guide for developers on how to create a repository plugin.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
===Prerequisites===&lt;br /&gt;
Before starting coding, it is necessary to know how to use repository administration pages and how to use the file picker.&lt;br /&gt;
&lt;br /&gt;
===First steps===&lt;br /&gt;
# Create a folder for your plugin in &#039;&#039;moodle/repository/&#039;&#039; e.g. &#039;&#039;moodle/repository/myplugin&#039;&#039;&lt;br /&gt;
# Create the following files and add them to the plugin folder:&lt;br /&gt;
#* &#039;&#039;repository.class.php&#039;&#039;&lt;br /&gt;
#* &#039;&#039;icon.png&#039;&#039; (the icon displayed in the file picker)&lt;br /&gt;
# Create the language file &#039;&#039;repository_myplugin.php&#039;&#039; and add it to the plugin folder, keeping the following folder structure:&lt;br /&gt;
#*&#039;&#039;moodle/repository/myplugin/lang/en_utf8/repository_myplugin.php&#039;&#039; or &#039;&#039;moodle/lang/en_utf8/repository_myplugin.php&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
===Overview===&lt;br /&gt;
&lt;br /&gt;
The 3 different parts to write&lt;br /&gt;
# Administration - You can customise the way administrators and users can configure their repositories. &lt;br /&gt;
# File picker integration - The core of your plugin, it will manage communication between Moodle and the repository service, and also the file picker display.&lt;br /&gt;
# I18n - Internationalization should be done at the same time as you&#039;re writing the other parts.&lt;br /&gt;
&lt;br /&gt;
==Administration APIs==&lt;br /&gt;
&lt;br /&gt;
As an example, let&#039;s create a Flickr plugin for accessing a public flickr account. The plugin will be called &amp;quot;Flickr Public&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Firstly the skeleton:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    &amp;lt;?php&lt;br /&gt;
    /**&lt;br /&gt;
     * repository_flickr_public class&lt;br /&gt;
     * Moodle user can access public flickr account&lt;br /&gt;
     *&lt;br /&gt;
     * @license http://www.gnu.org/copyleft/gpl.html GNU Public License&lt;br /&gt;
    */&lt;br /&gt;
    class repository_flickr_public extends repository {&lt;br /&gt;
    }&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Then consider the question &amp;quot;What does my plugin do?&amp;quot;&lt;br /&gt;
&lt;br /&gt;
In the Moodle file picker, we want to display some flickr public repositories directly linked to a flickr public account. For example &#039;&#039;My Public Flickr Pictures&#039;&#039;, and also &#039;&#039;My Friend&#039;s Flickr Pictures&#039;&#039;. When the user clicks on one of these repositories, the public pictures are displayed in the file picker.&lt;br /&gt;
&lt;br /&gt;
In order to access to a flickr public account, the plugin needs to know the email address of the Flickr public account owner. So the administrator will need to set an email address for every repository. Let&#039;s add an &amp;quot;email address&amp;quot; setting to every repository.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
//We tell the API that the repositories have specific settings: &amp;quot;email address&amp;quot;&lt;br /&gt;
    public static function get_instance_option_names() {&lt;br /&gt;
        return array(&#039;email_address&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
//We add an &amp;quot;email address&amp;quot; text box to the create/edit repository instance Moodle form&lt;br /&gt;
    public function instance_config_form(&amp;amp;$mform) {&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;email_address&#039;, get_string(&#039;emailaddress&#039;, &#039;repository_flickr_public&#039;));&lt;br /&gt;
        $mform-&amp;gt;addRule(&#039;email_address&#039;, get_string(&#039;required&#039;), &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
So at this moment all our Flickr Public Repositories will have a specific email address. However this is not enough. In order to communicate with Flickr, Moodle needs to know a Flickr API key (http://www.flickr.com/services/api/). This API key is the same for any repository. We could add it with the email address setting but the administrator would have to enter the same API key for every repository. Hopefully the administrator can add settings to the plugin level, impacting all repositories. The code is similar the repository instance settings:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
//We tell the API that the repositories have general settings: &amp;quot;api_key&amp;quot;&lt;br /&gt;
    public static function get_type_option_names() {&lt;br /&gt;
        return array(&#039;api_key&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
//We add an &amp;quot;api key&amp;quot; text box to the create/edit repository plugin Moodle form (also called a Repository type Moodle form)&lt;br /&gt;
    public function type_config_form(&amp;amp;$mform) {&lt;br /&gt;
        //the following line is needed in order to retrieve the API key value from the database when Moodle displays the edit form&lt;br /&gt;
        $api_key = get_config(&#039;flickr_public&#039;, &#039;api_key&#039;);&lt;br /&gt;
        $mform-&amp;gt;addElement(&#039;text&#039;, &#039;api_key&#039;, get_string(&#039;apikey&#039;, &#039;repository_flickr_public&#039;), &lt;br /&gt;
                           array(&#039;value&#039;=&amp;gt;$api_key,&#039;size&#039; =&amp;gt; &#039;40&#039;));&lt;br /&gt;
        $mform-&amp;gt;addRule(&#039;api_key&#039;, get_string(&#039;required&#039;), &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Have we finished yet?&lt;br /&gt;
&lt;br /&gt;
Yes! We have created everything necessary for the administration pages. But let&#039;s go further. It would be good if the user can enter any &amp;quot;Flickr public account email address&amp;quot; in the file picker. In fact we want to display in the file picker a Flickr Public repository that the Moodle administrator can never delete. Let&#039;s add:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
     //this function is only called one time, when the Moodle administrator add the Flickr Public Plugin into the Moodle site.&lt;br /&gt;
     public static function plugin_init() {&lt;br /&gt;
        //here we create a default repository instance. The last parameter is 1 in order to set the instance as readonly.&lt;br /&gt;
        repository_static_function(&#039;flickr_public&#039;,&#039;create&#039;, &#039;flickr_public&#039;, 0, get_system_context(), &lt;br /&gt;
                                    array(&#039;name&#039; =&amp;gt; &#039;default instance&#039;,&#039;email_address&#039; =&amp;gt; null),1);&lt;br /&gt;
     }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That&#039;s all - the administration part of our Flickr Public plugin is done. For your information, Box.net, Flickr, and Flickr Public all have similar administration APIs.&lt;br /&gt;
&lt;br /&gt;
===Functions===&lt;br /&gt;
All of the following functions are optional. If they&#039;re not implemented, your plugin will not have manual settings and will have only one instance displayed in the File Picker (The repository API creates this unique instance when the administrator add the plugin).&lt;br /&gt;
&lt;br /&gt;
==== get_instance_option_names====&lt;br /&gt;
&#039;&#039;This function must be declared static&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Return an array of strings. These strings are setting names. These settings are specific to an instance.&lt;br /&gt;
If the function returns an empty array, the API will consider that the plugin displays only one repository in the file picker.&lt;br /&gt;
Parent function returns an empty array.&lt;br /&gt;
&lt;br /&gt;
====instance_config_form(&amp;amp;$mform)====&lt;br /&gt;
This is for modifying the Moodle form displaying the settings specific to an instance.&lt;br /&gt;
&lt;br /&gt;
For example, to add a required text box called email_address:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$mform-&amp;gt;addElement(&#039;text&#039;, &#039;email_address&#039;, get_string(&#039;emailaddress&#039;, &#039;repository_flickr_public&#039;));&lt;br /&gt;
$mform-&amp;gt;addRule(&#039;email_address&#039;, $strrequired, &#039;required&#039;, null, &#039;client&#039;);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
   &lt;br /&gt;
&#039;&#039;Note: &#039;&#039;mform&#039;&#039; has by default a name text box (cannot be removed).&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Parent function does nothing.&lt;br /&gt;
&lt;br /&gt;
====get_type_option_names====&lt;br /&gt;
&#039;&#039;This function must be declared static&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
Return an array of string. These strings are setting names. These settings are shared by all instances.&lt;br /&gt;
Parent function return an empty array.&lt;br /&gt;
&lt;br /&gt;
====type_config_form(&amp;amp;$mform)====&lt;br /&gt;
This is for modifying the Moodle form displaying the plugin settings.&lt;br /&gt;
Similar to &#039;&#039;instance_config_form(&amp;amp;$mform)&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
====plugin_init()====&lt;br /&gt;
&#039;&#039;This function must be declared static&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
This function is called when the administrator adds the plugin. So unless the administrator deletes the plugin and re-adds it, it should be called only once.&lt;br /&gt;
Parent function does nothing.&lt;br /&gt;
&lt;br /&gt;
==File picker APIs==&lt;br /&gt;
=== Quick Start ===&lt;br /&gt;
&#039;&#039;&#039;To be completed&#039;&#039;&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
First of all, the File Picker using intensively Ajax you will need a easy way to debug. Install firePHP (MDL-16371) and make it works. It will save you a lot of time.&lt;br /&gt;
&lt;br /&gt;
Your first question when you write your plugin specification is &#039;Does the user need to log-in&#039;?&amp;lt;br&amp;gt;&lt;br /&gt;
.....&lt;br /&gt;
&lt;br /&gt;
For most of plugins, you need to establish a connection with the remote repository. This connection can be done into the get_listing(), constructor() function...&amp;lt;br&amp;gt;&lt;br /&gt;
.....&lt;br /&gt;
&lt;br /&gt;
You wanna display a list of files once the user is logged&amp;lt;br&amp;gt;&lt;br /&gt;
.... get_listing() .....&lt;br /&gt;
&lt;br /&gt;
You wanna retrieve the file that the user selected&amp;lt;br&amp;gt;&lt;br /&gt;
....&lt;br /&gt;
&lt;br /&gt;
Optional question that you should ask yourself is &#039;Does the user can execute a search&#039;&amp;lt;br&amp;gt;&lt;br /&gt;
.... search() ....&lt;br /&gt;
&lt;br /&gt;
===Functions you *MUST* override===&lt;br /&gt;
====__construct====&lt;br /&gt;
You may initialize your plugin here, such as:&lt;br /&gt;
# Get options from database&lt;br /&gt;
# Get user name and password from HTTP POST&lt;br /&gt;
&lt;br /&gt;
====get_listing($path=&amp;quot;&amp;quot;)====&lt;br /&gt;
This function will return a list of files, the list must be a array like this:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$list = array(&lt;br /&gt;
&#039;path&#039;=&amp;gt;&#039;/var/repo/&#039;,&lt;br /&gt;
&#039;manage&#039;=&amp;gt;&#039;http://webmgr.moodle.com&#039;,&lt;br /&gt;
&#039;list&#039;=&amp;gt; array(&lt;br /&gt;
    array(&#039;title&#039;=&amp;gt;&#039;filename1&#039;, &#039;date&#039;=&amp;gt;&#039;01/01/2009&#039;, &#039;size&#039;=&amp;gt;&#039;10MB&#039;, &#039;source&#039;=&amp;gt;&#039;http://www.moodle.com/dl.rar&#039;),&lt;br /&gt;
    array(&#039;title&#039;=&amp;gt;&#039;folder&#039;, &#039;date&#039;=&amp;gt;&#039;01/01/2009&#039;, &#039;size&#039;=&amp;gt;&#039;0&#039;, &#039;children&#039;=&amp;gt;array())&lt;br /&gt;
)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&#039;&#039;&#039;The full specification:&#039;&#039;&#039;&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
 array(&lt;br /&gt;
   // iframe&lt;br /&gt;
   &#039;iframe&#039; =&amp;gt; (string) the url of the iframe, once this option is set, the right panel in file picker will display a iframe instead tree view or thumbnail view, in this mode, plugin developers could design the layout of repository freely, there is a function named repository_downoload_btn, it will display a download button which can help to move files to moodle file system.&lt;br /&gt;
   // the current path&lt;br /&gt;
   &#039;path&#039; =&amp;gt; (string) path for the current folder&lt;br /&gt;
   // dynload tells file picker to fetch list dynamically, when user click&lt;br /&gt;
   // the folder, it will send a ajax request to server side.&lt;br /&gt;
   &#039;dynload&#039; =&amp;gt; (bool) use dynamic loading,&lt;br /&gt;
   // will display a link in file picker&lt;br /&gt;
   &#039;manage&#039; =&amp;gt; (string) url of the file manager,&lt;br /&gt;
   // set to true, the login link will be removed from file picker&lt;br /&gt;
   &#039;nologin&#039; =&amp;gt; (bool) requires login,&lt;br /&gt;
   // set to true, the search link will be removed from file picker&lt;br /&gt;
   &#039;nosearch&#039; =&amp;gt; (bool) no search link,&lt;br /&gt;
   // set this option will display a upload form in file picker&lt;br /&gt;
   // only used in upload plugin currently&lt;br /&gt;
   &#039;upload&#039; =&amp;gt; array( // upload manager&lt;br /&gt;
     &#039;name&#039; =&amp;gt; (string) label of the form element,&lt;br /&gt;
     &#039;id&#039; =&amp;gt; (string) id of the form element&lt;br /&gt;
   ),&lt;br /&gt;
   // file picker will build a file tree according this &lt;br /&gt;
   // list&lt;br /&gt;
   &#039;list&#039; =&amp;gt; array(&lt;br /&gt;
     array( // file&lt;br /&gt;
       &#039;title&#039; =&amp;gt; (string) file name,&lt;br /&gt;
       &#039;date&#039; =&amp;gt; (string) file last modification time, usually userdate(...),&lt;br /&gt;
       &#039;size&#039; =&amp;gt; (int) file size,&lt;br /&gt;
       &#039;thumbnail&#039; =&amp;gt; (string) url to thumbnail for the file,&lt;br /&gt;
       &#039;thumbnail_width&#039; =&amp;gt; (int) the width of the thumbnail image,&lt;br /&gt;
       &#039;source&#039; =&amp;gt; plugin-dependent unique path to the file (id, url, path, etc.),&lt;br /&gt;
       &#039;url&#039;=&amp;gt; the accessible url of file&lt;br /&gt;
     ),&lt;br /&gt;
     array( // folder - same as file, but no &#039;source&#039;.&lt;br /&gt;
       &#039;title&#039; =&amp;gt; (string) folder name,&lt;br /&gt;
       &#039;path&#039; =&amp;gt; (string) path to this folder&lt;br /&gt;
       &#039;date&#039; =&amp;gt; (string) folder last modification time, usually userdate(...),&lt;br /&gt;
       &#039;size&#039; =&amp;gt; 0,&lt;br /&gt;
       &#039;thumbnail&#039; =&amp;gt; (string) url to thumbnail for the folder,&lt;br /&gt;
       &#039;children&#039; =&amp;gt; array( // an empty folder needs to have &#039;children&#039; defined, but empty.&lt;br /&gt;
         // content (files and folders)&lt;br /&gt;
       )&lt;br /&gt;
     ),&lt;br /&gt;
   )&lt;br /&gt;
 )&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
Dynamically loading&lt;br /&gt;
Some repositories contain many files which cannot load in one time, in this case, we need dynamically loading to fetch them step by step, files in subfolder won&#039;t be listed until user click the folder in file picker treeview.&lt;br /&gt;
&lt;br /&gt;
As a plug-in developer, if you set dynload flag as &#039;&#039;&#039;true&#039;&#039;&#039;, you should return files and folders (set children as a null array) in current path instead of building the whole file tree.&lt;br /&gt;
&lt;br /&gt;
Example of dynamically loading&lt;br /&gt;
See [http://cvs.moodle.org/moodle/repository/alfresco/repository.class.php?view=log Alfresco] plug-in&lt;br /&gt;
&lt;br /&gt;
===Functions you can override===&lt;br /&gt;
====print_login====&lt;br /&gt;
This function will help to print a login form, for the Ajax file picker, this function will return a&lt;br /&gt;
PHP array to define this form.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
    public function print_login(){&lt;br /&gt;
        if ($this-&amp;gt;options[&#039;ajax&#039;]) {&lt;br /&gt;
            $user_field-&amp;gt;label = get_string(&#039;username&#039;, &#039;repository_boxnet&#039;).&#039;: &#039;;&lt;br /&gt;
            $user_field-&amp;gt;id    = &#039;box_username&#039;;&lt;br /&gt;
            $user_field-&amp;gt;type  = &#039;text&#039;;&lt;br /&gt;
            $user_field-&amp;gt;name  = &#039;boxusername&#039;;&lt;br /&gt;
            $user_field-&amp;gt;value = $ret-&amp;gt;username;&lt;br /&gt;
            &lt;br /&gt;
            $passwd_field-&amp;gt;label = get_string(&#039;password&#039;, &#039;repository_boxnet&#039;).&#039;: &#039;;&lt;br /&gt;
            $passwd_field-&amp;gt;id    = &#039;box_password&#039;;&lt;br /&gt;
            $passwd_field-&amp;gt;type  = &#039;password&#039;;&lt;br /&gt;
            $passwd_field-&amp;gt;name  = &#039;boxpassword&#039;;&lt;br /&gt;
&lt;br /&gt;
            $ret = array();&lt;br /&gt;
            $ret[&#039;login&#039;] = array($user_field, $passwd_field);&lt;br /&gt;
            return $ret;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
This will help to generate a form by file picker which contains user name and password input elements.&lt;br /&gt;
&lt;br /&gt;
If your plugin don&#039;t require logging in, you don&#039;t need to override it, it will call get_listing to list files automatically by default.&lt;br /&gt;
====check_login====&lt;br /&gt;
This function will return a boolean value to tell Moodle whether the user has logged in.&lt;br /&gt;
By default, this function will return true.&lt;br /&gt;
====logout====&lt;br /&gt;
When a user clicks the logout button in file picker, this function will be called. You may clean up the session or disconnect the connection with remote server here.&lt;br /&gt;
====global_search====&lt;br /&gt;
Moodle Repository API supports global search, this function will return a boolean value to tell Moodle if this plugin is ready to search. By default, it will return false - you need to override it to enable global search.&lt;br /&gt;
====print_search====&lt;br /&gt;
When a user clicks the search button on file picker, this function will be called to return a search form. By default, it will create a form with single search bar - you can override it to create a advanced search form.&lt;br /&gt;
====search====&lt;br /&gt;
This function will do the searching job. You can obtain the POST parameters from the from the form you created in print_search function&lt;br /&gt;
This function will return a file list exactly like the one from get_listing.&lt;br /&gt;
====get_file====&lt;br /&gt;
When a user clicks the &amp;quot;Get&amp;quot; button to transfer the file, this function will be called. Basically, it will download a file to Moodle - you can override it to modify the file then move it to a better location.&lt;br /&gt;
====get_name====&lt;br /&gt;
This function will return the name of the repository instance.&lt;br /&gt;
&lt;br /&gt;
== I18n - Internationalization ==&lt;br /&gt;
These following strings are required in &#039;&#039;moodle/repository/myplugin/lang/en_utf8/repository_myplugin.php&#039;&#039; or &#039;&#039;moodle/lang/en_utf8/repository_myplugin.php&#039;&#039;:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$string[&#039;configplugin&#039;] = &#039;Flickr Public configuration&#039;;&lt;br /&gt;
$string[&#039;repositorydesc&#039;] = &#039;A Flickr public repository&#039;;&lt;br /&gt;
$string[&#039;repositoryname&#039;] = &#039;Flickr Public&#039;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Standard repository plugins ==&lt;br /&gt;
This is the functional specification list of the officially supported repository plugins.&lt;br /&gt;
For each plugins, the two mains part we are interested in are:&lt;br /&gt;
* How do I administrate the plugin? See [[Development:Repository_Administration_Specification| Repository Administration Specification - UC001-3]]&lt;br /&gt;
* How do I set up an account for this repository? See [[Development:Repository_Interface_for_Moodle/Course/User| Repository Interface for Moodle/Course/User]]&lt;br /&gt;
&lt;br /&gt;
=== Functional specifications ===&lt;br /&gt;
*[[Development:Box.net Repository Plugin|Box.net Repository Plugin]]&lt;br /&gt;
*[[Development:Flickr Repository Plugin|Flickr Repository Plugin]]&lt;br /&gt;
*[[Development:Moodle Repository Plugin|Moodle Local Repository Plugin]]&lt;br /&gt;
*[[Development:MNET Repository Plugin|Moodle MNET Repository Plugin]]&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
*[[Development:Repository API| Repository API]]&lt;br /&gt;
*[[Development:Repository_Interface_for_Moodle/Course/User| Repository Interface for Moodle/Course/User]]&lt;br /&gt;
*[[QA:Use Case Number Attribution| Use Case Number Attribution]]&lt;br /&gt;
* MDL-16543 - A list of officially supported repository plugins&lt;br /&gt;
&lt;br /&gt;
[[Category:Repositories]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Grades_FAQ&amp;diff=46554</id>
		<title>Grades FAQ</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Grades_FAQ&amp;diff=46554"/>
		<updated>2008-11-10T13:45:21Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Modules */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Grades}}&lt;br /&gt;
== General ==&lt;br /&gt;
&lt;br /&gt;
===How can I change how grades are displayed?===&lt;br /&gt;
&lt;br /&gt;
Grades may be displayed as as actual grades, as percentages (in reference to the minimum and maximum grades) or as letters.&lt;br /&gt;
&lt;br /&gt;
The default grade display type for the site is set by an administrator in &#039;&#039;Administration &amp;gt; Grades &amp;gt; [[Grade item settings]]&#039;&#039;. However, this may be changed at course level.&lt;br /&gt;
&lt;br /&gt;
To change how grades are displayed for particular [[Grade items|grade items]], or category and course summaries (called aggregations):&lt;br /&gt;
&lt;br /&gt;
# Follow the grades link in the course administration block.&lt;br /&gt;
# Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# Click the edit icon for the grade item, category total or course total.&lt;br /&gt;
# From the Grade display type menu, select real (for actual grades), percentage or letter.&lt;br /&gt;
# Click the &amp;quot;Save changes&amp;quot; button at the bottom of the page.&lt;br /&gt;
&lt;br /&gt;
Alternatively, to change how grades are displayed for the whole course:&lt;br /&gt;
&lt;br /&gt;
# Follow the grades link in the course administration block.&lt;br /&gt;
# Select &amp;quot;Course settings&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# From the Grade display type menu, select real (for actual grades), percentage or letter.&lt;br /&gt;
# Click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
===How can I hide entered grades until a specified date?===&lt;br /&gt;
&lt;br /&gt;
To set a &amp;quot;Hidden until&amp;quot; date:&lt;br /&gt;
&lt;br /&gt;
#Access the course gradebook via the grades link in the course administration block.&lt;br /&gt;
#Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
#Click on the edit icon opposite the activity for which a &amp;quot;Hidden until&amp;quot; date is to be set.&lt;br /&gt;
#On the edit grade item page, ensure that advanced settings are displayed. (Click the &amp;quot;Show advanced&amp;quot; button if not.)&lt;br /&gt;
#Enable the &amp;quot;Hidden until&amp;quot; setting by unchecking the disable checkbox, then set a date.&lt;br /&gt;
#Click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
=== Is it possible to show the teachers/administrators&#039; grades in the grader report? ===&lt;br /&gt;
Yes, at the site level you can define which roles will appear in the grader report. This can be found in [[General_grade_settings#Graded_Roles|Administration &amp;gt; Grades &amp;gt; General settings]]. Also read [http://moodle.org/mod/forum/discuss.php?d=92612 this discussion] for some more ideas.&lt;br /&gt;
&lt;br /&gt;
===Why can&#039;t I change a grade within an assignment after changing it in the gradebook?===&lt;br /&gt;
&lt;br /&gt;
When you edit a grade directly in the gradebook, an &amp;quot;overridden&amp;quot; flag is set, meaning that the grade can no longer be changed from within the assignment.&lt;br /&gt;
&lt;br /&gt;
However, the flag can be removed by turning editing on in the [[Grader report|grader report]], then clicking the [[Grade editing|edit grade]] icon, unchecking the overridden box and saving the changes.&lt;br /&gt;
&lt;br /&gt;
===How do I get groups to show up in the grader report?===&lt;br /&gt;
&lt;br /&gt;
For groups to show up in the grader report, group mode should be set to visible or separate groups in the [[Course settings|course settings]]. This will result in a groups dropdown menu being displayed, enabling a teacher to view the grades of all participants, or only the grades for a selected group.&lt;br /&gt;
&lt;br /&gt;
== Reports ==&lt;br /&gt;
=== How do I create my own custom gradebook reports? ===&lt;br /&gt;
Here is a [[Development:Gradebook_Report_Tutorial|tutorial]] explaining all the main steps involved.&lt;br /&gt;
&lt;br /&gt;
== Aggregation ==&lt;br /&gt;
=== I can&#039;t find where to change the aggregation type for my gradebook categories! ===&lt;br /&gt;
Each category has an aggregation type, which can be changed through that category&#039;s &amp;quot;edit&amp;quot; page. To access that page, you must use one of 2 ways:&lt;br /&gt;
&lt;br /&gt;
1. In the grader report, turn &amp;quot;Editing&amp;quot; on, then click the little &amp;quot;hand&amp;quot; icon next to the category whose aggregation you want to change&lt;br /&gt;
2. In the &amp;quot;Edit categories and Items&amp;quot; page (accessible through the &amp;quot;choose an action&amp;quot; menu, top left), you see a tree view of the categories and items in your gradebook. The top category is the course category. Each category also has a &amp;quot;hand&amp;quot; icon, which leads to the category edit page&lt;br /&gt;
&lt;br /&gt;
=== How can I grade some of my activities without the results affecting my students&#039; course total? ===&lt;br /&gt;
#Create two [[Grade categories]], one for your &amp;quot;activities still being graded,&amp;quot; and one for your &amp;quot;released&amp;quot; activities.&lt;br /&gt;
#Ensure that &amp;quot;Aggregate including subcategories&amp;quot; (an advanced option) is unchecked for your top level course grade category.&lt;br /&gt;
##Where is this?  In gradebook (grader report), in the upper right corner, click the &amp;quot;Turn Editing On&amp;quot; button.&lt;br /&gt;
##Click the edit icon next to the &amp;quot;course category&amp;quot; (usually your course name, just above the quiz names and below all the clickable links that were revealed when you turned editing on)&lt;br /&gt;
##Then make sure you have the &amp;quot;Show Advanced&amp;quot; option turned on.&lt;br /&gt;
#Edit the &amp;quot;activities still being graded&amp;quot; category&#039;s &amp;quot;course total&amp;quot; item. (This is one of the categories you created above.)&lt;br /&gt;
##Where is this?  Look for the edit icon under &amp;quot;category total&amp;quot; that is below this category&#039;s name&lt;br /&gt;
#Set the &amp;quot;grade type&amp;quot; to &amp;quot;none&amp;quot;.&lt;br /&gt;
#Tick the &amp;quot;Hidden&amp;quot; checkbox.&lt;br /&gt;
#Save your changes.&lt;br /&gt;
#Move all your activities being graded in the &amp;quot;activities still being graded&amp;quot;  category.&lt;br /&gt;
#Move all your activities already graded in the &amp;quot;released&amp;quot; category.&lt;br /&gt;
&lt;br /&gt;
Note: I rewrote this a bit, to help people find where things are.  However, this method didn&#039;t seem to work for me on Moodle 1.9.&lt;br /&gt;
&lt;br /&gt;
=== My student completed only one activity out of 5, but his course total shows 100%. How do I show a more &amp;quot;progressive&amp;quot; course total? ===&lt;br /&gt;
By default, only non-empty grades are aggregated, the others are ignored. However, you can change this setting as well as others that affect the course total, by turning &amp;quot;Editing&amp;quot; on in the grader report, and clicking the &amp;quot;Edit&amp;quot; icon next to the course category (the very top row of the grader report).&lt;br /&gt;
&lt;br /&gt;
You can untick the box &amp;quot;Aggregate only non-empty grades&amp;quot; if you want to show a more &amp;quot;progressive&amp;quot; score for each student. Their empty grades will count as a 0 and will be counted in the course mean/total.&lt;br /&gt;
 &lt;br /&gt;
If you prefer to show a sum of points, rather than a percentage, you can change the course category&#039;s aggregation method to &amp;quot;Sum of grades&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== How can I display the average grade for my course categories (not grade categories)? ===&lt;br /&gt;
In Moodle 1.9 there is no way to aggregate course totals within each category. The gradebook is course-centered, and there is currently no User Interface for showing grades within an entire course category at once.&lt;br /&gt;
&lt;br /&gt;
=== How can I setup weighted assignments? ===&lt;br /&gt;
See [[Using &amp;quot;Weighted Mean of Grades&amp;quot; to weight categories containing assignments]].&lt;br /&gt;
&lt;br /&gt;
== Categories ==&lt;br /&gt;
=== How many depths of categories/subcategories can I create? ===&lt;br /&gt;
There is no programmatic limit, but there are practical limits. Very deeply nested structures are difficult to manage. 3 levels of categories should be sufficient for most situations. Note that there is always at least one level of categories, since the Course category always encompasses all other categories and grade items, can cannot be deleted.&lt;br /&gt;
&lt;br /&gt;
=== I can&#039;t find setting X in the grade category edit page! Where is it? ===&lt;br /&gt;
If a setting documented on the [[Grade categories]] page does not appear on your edit page, it may mean that it is set globally in your site. See [[Grade_category_settings#Forcing_settings|Forcing settings]] for more information.&lt;br /&gt;
&lt;br /&gt;
== Outcomes ==&lt;br /&gt;
=== I have just upgraded to Moodle 1.9, and I want to set up an outcome item for my course. What are the steps required? ===&lt;br /&gt;
#[[General_grade_settings#Enable_outcomes|Administration &amp;gt; Grades &amp;gt; General settings &amp;gt; Enable outcomes]]&lt;br /&gt;
#[[Scales#Creating_a_new_scale|Create a scale]]&lt;br /&gt;
#Create a course outcome (read the [[Outcomes| outcomes documentation]] for instructions). Assign to it the scale you just created.&lt;br /&gt;
#Assign the outcome to your course&lt;br /&gt;
#Enter the &amp;quot;Grades&amp;quot; section of your course, from the course administration block&lt;br /&gt;
#In the Actions menu (top left), select Edit -&amp;gt; Categories and Items&lt;br /&gt;
#Click &amp;quot;Add outcome item&amp;quot;&lt;br /&gt;
#Follow the instructions of the [[Outcome items|outcome items documentation]] to create the outcome item&lt;br /&gt;
&lt;br /&gt;
You can now give your students a rating on the outcome dimension you just created. If you created a standard outcome, you will be able to use it in other courses and follow your students&#039; performance across these courses.&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
=== The activity module (Module name) doesn&#039;t support grading. How can I give my students a grade anyway? ===&lt;br /&gt;
You can create a [[Grade_items#Manual_grade_items|grade item]] manually in the gradebook. You will have to grade your students through the [[Grader report]] interface (in editing mode).&lt;br /&gt;
&lt;br /&gt;
=== I just graded some of my students using the (Module name) interface, but the results aren&#039;t showing up in the grader report. What&#039;s going on? ===&lt;br /&gt;
Here are some of the possible reasons:&lt;br /&gt;
&lt;br /&gt;
#The corresponding [[Grade items|grade item]] is [[Grade_locking#In_grade_items|locked]], or its parent [[Grade categories|category]] is [[Grade_locking#In_grade_categories|locked]].&lt;br /&gt;
#The module code is not using the [[Development:Grades#API_for_communication_with_modules.2Fblocks|gradebook API]] correctly&lt;br /&gt;
&lt;br /&gt;
=== I just created a new assignment with the &amp;quot;Grade&amp;quot; setting set to &amp;quot;No grade&amp;quot;, but it still appears in the gradebook ===&lt;br /&gt;
The reason is that the gradebook is now the place where both numerical and textual types of feedback are recorded for all activity modules. The word &amp;quot;Grading&amp;quot; in assignment relates only to numerical grades, but the ability to give text feedback remains, and must be recorded in the gradebook. This is why a grade item is created for it. You can hide the grade item if you do not want it to appear in the user reports.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[Gradebook 1.9 Tutorial]]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?id=2122 Gradebook forum]&lt;br /&gt;
&lt;br /&gt;
Using Moodle forum discussions:&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=102609 Can I aggregate only non hidden items?]&lt;br /&gt;
&lt;br /&gt;
[[Category:FAQ]]&lt;br /&gt;
&lt;br /&gt;
[[ca:PMF de les qualificacions]]&lt;br /&gt;
[[fr:FAQ des notes]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Talk:experimental:_SOP_multicats&amp;diff=46546</id>
		<title>Talk:experimental: SOP multicats</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Talk:experimental:_SOP_multicats&amp;diff=46546"/>
		<updated>2008-11-10T09:47:48Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;You don&#039;t need to use calculations, in fact. You can simply set the &amp;quot;uncategorised&amp;quot; category&#039;s grade type to &amp;quot;None&amp;quot;. This way, it will not be counted in the course total. You can also hide this category to prevent it from appearing in the students&#039; report.[[User:Nicolas Connault|Nicolas Connault]] 03:47, 10 November 2008 (CST)&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Talk:experimental:_SOP_multicats&amp;diff=46545</id>
		<title>Talk:experimental: SOP multicats</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Talk:experimental:_SOP_multicats&amp;diff=46545"/>
		<updated>2008-11-10T09:47:37Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: New page: You don&amp;#039;t need to use calculations, in fact. You can simply set the &amp;quot;uncategorised&amp;quot; category&amp;#039;s grade type to &amp;quot;None&amp;quot;. This way, it will not be counted in the course total. You can also hide...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;You don&#039;t need to use calculations, in fact. You can simply set the &amp;quot;uncategorised&amp;quot; category&#039;s grade type to &amp;quot;None&amp;quot;. This way, it will not be counted in the course total. You can also hide this category to prevent it from appearing in the students&#039; report.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=User:Nicolas_Connault&amp;diff=46448</id>
		<title>User:Nicolas Connault</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=User:Nicolas_Connault&amp;diff=46448"/>
		<updated>2008-11-07T13:44:24Z</updated>

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

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This FAQ asks begins answering the question  &#039;&#039;&#039;Why is the new gradebook so complicated?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
There has been considerable discussion about usability as Moodle has grown, including questioning whether adding new features necessarily requires a more complicated interface.  Some have argued (including this author) that more field testing, carefully chosen defaults, and progressive revealing of features (including an &amp;quot;advanced&amp;quot; button) can make it much easier for new users while making it possible to make available features selectivity available as needed.  During the Beta process, progress was made on this, but additional improvements are probably quite possible.&lt;br /&gt;
&lt;br /&gt;
An obvious example would have been to retain the &amp;lt;1.9 interface as a legacy option, but built on top of the new code.  The counter-argument is to make all options available to every user so that they know that the feature is there, but this has a high cost in terms of usability as a product becomes feature-rich. --[[User:Gary Anderson|Gary Anderson]] 10:59, 29 March 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
:We couldn&#039;t retain the &amp;lt;1.9 interface, we would have had to re-code it entirely. The main reason for developing the new gradebook was internal, to enable long-term scalability, extensibility and consistency. We don&#039;t expect end users to understand or be interested in what goes on behind the User Interface, in the database, in the backend code, but it is true that usability has suffered as a result. The good news is that our efforts are now entirely focused on usability instead of these other important considerations. [[User:Nicolas Connault|Nicolas Connault]] 07:39, 7 November 2008 (CST)&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46156</id>
		<title>Development:Gradebook interface improvements for Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46156"/>
		<updated>2008-11-04T10:41:50Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Advanced features */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
One of the goals of Moodle 2.0 is to improve the &#039;&#039;&#039;usability&#039;&#039;&#039; of the gradebook introduced in 1.9.&lt;br /&gt;
&lt;br /&gt;
The purpose of this page is to present a list of proposed improvements, and gather feedback on these implementations before they make it into this important release. These proposed improvements are based on bug reports and forum posts that have been coming in since the 1.9 release.&lt;br /&gt;
&lt;br /&gt;
We would really like your feedback on:&lt;br /&gt;
# the changes already proposed here&lt;br /&gt;
# any issues that you feel are not yet addressed &lt;br /&gt;
&lt;br /&gt;
Please join the discussions and post your suggestions in the [http://moodle.org/mod/forum/view.php?f=397 Gradebook forum].&lt;br /&gt;
&lt;br /&gt;
==Common frustrations==&lt;br /&gt;
Below are some of the most often reported usability issues in the 1.9 gradebook, which we are trying to address. Several of these are grouped as sub-tasks under MDL-16913.&lt;br /&gt;
&lt;br /&gt;
===Assigning weights to categories and grade items===&lt;br /&gt;
This is the most common frustration. Weights are used extensively in many institutions, and were used in 1.8. However, they are difficult to understand in 1.9, and difficult to set up. This issue is discussed extensively in MDL-15680.&lt;br /&gt;
&lt;br /&gt;
===Moving items to categories===&lt;br /&gt;
Currently, the [[Edit categories and items]] page lets you move only one grade item or category at a time. It takes two page refreshes and two mouse clicks per move. This is very tedious and time-consuming when many items have to be moved around. MDL-13775 and MDL-12502 address this problem.&lt;br /&gt;
&lt;br /&gt;
===Removing the &#039;&#039;overridden&#039;&#039; attribute of individual grades===&lt;br /&gt;
When grades are imported into an existing gradebook, or when grades are manually edited in the grader report, these grades become [[Grade_editing#Overridden|overridden]], which prevents linked activity modules from updating this grade. Removing this attribute is very time-consuming, since one must enter the edit page of each individual grade, untick the checkbox and submit the form. This issue is discussed in [http://moodle.org/mod/forum/discuss.php?d=109636 this forum thread].&lt;br /&gt;
&lt;br /&gt;
===Viewing the overall contribution of each category and item to the course aggregation===&lt;br /&gt;
The tracker issue MDL-13777 reports the difficulty in seeing the contribution of each grade item and category to the course total. Calculations and weights may apply, which are not visible in any of the current reports except in each individual category or item&#039;s edit page.&lt;br /&gt;
&lt;br /&gt;
==Patch for Edit Categories and Items page==&lt;br /&gt;
Following is a proposed patch to the &#039;&#039;&#039;Edit categories and items&#039;&#039;&#039; page in the 1.9 gradebook. It addresses many usability issues while remaining simple. The following features are currently implemented:&lt;br /&gt;
&lt;br /&gt;
===Simple features===&lt;br /&gt;
[[Image:gradebook_categories_simple.png|600px]]&lt;br /&gt;
*Items and categories are displayed in a table instead of a list. This improves readability and will make drag and drop easier to implement.&lt;br /&gt;
*All advanced features are hidden by default, so that the interface by default is almost identical to the original&lt;br /&gt;
*An additional column has checkboxes for grade items. All selected items can then be moved to one of the existing grade categories for the current course. Javascript &amp;quot;Select all/Select None&amp;quot; links replace these checkboxes for categories, to make it faster to select or de-select items.&lt;br /&gt;
&lt;br /&gt;
===Advanced features===&lt;br /&gt;
[[Image:gradebook_categories_advanced.png|600px]]&lt;br /&gt;
&lt;br /&gt;
*[[Category aggregation|Grade category aggregation type]]: changing this reloads the page, because the grade item weights/extra credits only apply to some of these aggregation types.&lt;br /&gt;
*Weight or extra credit: depending on the parent category&#039;s aggregation type, each category and grade item may have an input field or a checkbox for weight or extra credit. All these can be edited, then submitted with one click.&lt;br /&gt;
*Grade range is displayed for information purposes, but cannot be edited through this interface (it is most often controlled by the linked activity)&lt;br /&gt;
*[[Grade_categories#Aggregate_only_non-empty_grades|Aggregate only non-empty grades]]&lt;br /&gt;
*[[Grade_categories#Aggregate_including_sub-categories|Aggregate including sub-categories]]&lt;br /&gt;
*[[Grade_categories#Include_outcomes_in_aggregation|Include outcomes in aggregation]]&lt;br /&gt;
*[[Grade_categories#Drop_the_lowest|Drop the lowest]]&lt;br /&gt;
*[[Grade_categories#Keep_the_highest|Keep the highest]]&lt;br /&gt;
*[[Grade items|Multiplicator]]&lt;br /&gt;
*[[Grade_items|Offset]]&lt;br /&gt;
&lt;br /&gt;
All these settings can be changed then submitted with only one page reload. Just be mindful that changing a category&#039;s aggregation type will reload the page and you will lose any non-submitted changes you made to the other settings.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=File:gradebook_categories_advanced.png&amp;diff=46155</id>
		<title>File:gradebook categories advanced.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=File:gradebook_categories_advanced.png&amp;diff=46155"/>
		<updated>2008-11-04T10:41:22Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: The new &amp;quot;Edit categories and items&amp;quot; interface, with advanced settings displayed.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The new &amp;quot;Edit categories and items&amp;quot; interface, with advanced settings displayed.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46154</id>
		<title>Development:Gradebook interface improvements for Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46154"/>
		<updated>2008-11-04T10:40:51Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Simple features */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
One of the goals of Moodle 2.0 is to improve the &#039;&#039;&#039;usability&#039;&#039;&#039; of the gradebook introduced in 1.9.&lt;br /&gt;
&lt;br /&gt;
The purpose of this page is to present a list of proposed improvements, and gather feedback on these implementations before they make it into this important release. These proposed improvements are based on bug reports and forum posts that have been coming in since the 1.9 release.&lt;br /&gt;
&lt;br /&gt;
We would really like your feedback on:&lt;br /&gt;
# the changes already proposed here&lt;br /&gt;
# any issues that you feel are not yet addressed &lt;br /&gt;
&lt;br /&gt;
Please join the discussions and post your suggestions in the [http://moodle.org/mod/forum/view.php?f=397 Gradebook forum].&lt;br /&gt;
&lt;br /&gt;
==Common frustrations==&lt;br /&gt;
Below are some of the most often reported usability issues in the 1.9 gradebook, which we are trying to address. Several of these are grouped as sub-tasks under MDL-16913.&lt;br /&gt;
&lt;br /&gt;
===Assigning weights to categories and grade items===&lt;br /&gt;
This is the most common frustration. Weights are used extensively in many institutions, and were used in 1.8. However, they are difficult to understand in 1.9, and difficult to set up. This issue is discussed extensively in MDL-15680.&lt;br /&gt;
&lt;br /&gt;
===Moving items to categories===&lt;br /&gt;
Currently, the [[Edit categories and items]] page lets you move only one grade item or category at a time. It takes two page refreshes and two mouse clicks per move. This is very tedious and time-consuming when many items have to be moved around. MDL-13775 and MDL-12502 address this problem.&lt;br /&gt;
&lt;br /&gt;
===Removing the &#039;&#039;overridden&#039;&#039; attribute of individual grades===&lt;br /&gt;
When grades are imported into an existing gradebook, or when grades are manually edited in the grader report, these grades become [[Grade_editing#Overridden|overridden]], which prevents linked activity modules from updating this grade. Removing this attribute is very time-consuming, since one must enter the edit page of each individual grade, untick the checkbox and submit the form. This issue is discussed in [http://moodle.org/mod/forum/discuss.php?d=109636 this forum thread].&lt;br /&gt;
&lt;br /&gt;
===Viewing the overall contribution of each category and item to the course aggregation===&lt;br /&gt;
The tracker issue MDL-13777 reports the difficulty in seeing the contribution of each grade item and category to the course total. Calculations and weights may apply, which are not visible in any of the current reports except in each individual category or item&#039;s edit page.&lt;br /&gt;
&lt;br /&gt;
==Patch for Edit Categories and Items page==&lt;br /&gt;
Following is a proposed patch to the &#039;&#039;&#039;Edit categories and items&#039;&#039;&#039; page in the 1.9 gradebook. It addresses many usability issues while remaining simple. The following features are currently implemented:&lt;br /&gt;
&lt;br /&gt;
===Simple features===&lt;br /&gt;
[[Image:gradebook_categories_simple.png|600px]]&lt;br /&gt;
*Items and categories are displayed in a table instead of a list. This improves readability and will make drag and drop easier to implement.&lt;br /&gt;
*All advanced features are hidden by default, so that the interface by default is almost identical to the original&lt;br /&gt;
*An additional column has checkboxes for grade items. All selected items can then be moved to one of the existing grade categories for the current course. Javascript &amp;quot;Select all/Select None&amp;quot; links replace these checkboxes for categories, to make it faster to select or de-select items.&lt;br /&gt;
&lt;br /&gt;
===Advanced features===&lt;br /&gt;
[[Image:gradebook_categories_advanced.png]]&lt;br /&gt;
*[[Category aggregation|Grade category aggregation type]]: changing this reloads the page, because the grade item weights/extra credits only apply to some of these aggregation types.&lt;br /&gt;
*Weight or extra credit: depending on the parent category&#039;s aggregation type, each category and grade item may have an input field or a checkbox for weight or extra credit. All these can be edited, then submitted with one click.&lt;br /&gt;
*Grade range is displayed for information purposes, but cannot be edited through this interface (it is most often controlled by the linked activity)&lt;br /&gt;
*[[Grade_categories#Aggregate_only_non-empty_grades|Aggregate only non-empty grades]]&lt;br /&gt;
*[[Grade_categories#Aggregate_including_sub-categories|Aggregate including sub-categories]]&lt;br /&gt;
*[[Grade_categories#Include_outcomes_in_aggregation|Include outcomes in aggregation]]&lt;br /&gt;
*[[Grade_categories#Drop_the_lowest|Drop the lowest]]&lt;br /&gt;
*[[Grade_categories#Keep_the_highest|Keep the highest]]&lt;br /&gt;
*[[Grade items|Multiplicator]]&lt;br /&gt;
*[[Grade_items|Offset]]&lt;br /&gt;
&lt;br /&gt;
All these settings can be changed then submitted with only one page reload. Just be mindful that changing a category&#039;s aggregation type will reload the page and you will lose any non-submitted changes you made to the other settings.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=File:gradebook_categories_simple.png&amp;diff=46153</id>
		<title>File:gradebook categories simple.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=File:gradebook_categories_simple.png&amp;diff=46153"/>
		<updated>2008-11-04T10:39:51Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: The new &amp;quot;Edit categories and items&amp;quot; interface, with advanced settings hidden.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The new &amp;quot;Edit categories and items&amp;quot; interface, with advanced settings hidden.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46152</id>
		<title>Development:Gradebook interface improvements for Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46152"/>
		<updated>2008-11-04T10:39:10Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Patch for Edit Categories and Items page */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
One of the goals of Moodle 2.0 is to improve the &#039;&#039;&#039;usability&#039;&#039;&#039; of the gradebook introduced in 1.9.&lt;br /&gt;
&lt;br /&gt;
The purpose of this page is to present a list of proposed improvements, and gather feedback on these implementations before they make it into this important release. These proposed improvements are based on bug reports and forum posts that have been coming in since the 1.9 release.&lt;br /&gt;
&lt;br /&gt;
We would really like your feedback on:&lt;br /&gt;
# the changes already proposed here&lt;br /&gt;
# any issues that you feel are not yet addressed &lt;br /&gt;
&lt;br /&gt;
Please join the discussions and post your suggestions in the [http://moodle.org/mod/forum/view.php?f=397 Gradebook forum].&lt;br /&gt;
&lt;br /&gt;
==Common frustrations==&lt;br /&gt;
Below are some of the most often reported usability issues in the 1.9 gradebook, which we are trying to address. Several of these are grouped as sub-tasks under MDL-16913.&lt;br /&gt;
&lt;br /&gt;
===Assigning weights to categories and grade items===&lt;br /&gt;
This is the most common frustration. Weights are used extensively in many institutions, and were used in 1.8. However, they are difficult to understand in 1.9, and difficult to set up. This issue is discussed extensively in MDL-15680.&lt;br /&gt;
&lt;br /&gt;
===Moving items to categories===&lt;br /&gt;
Currently, the [[Edit categories and items]] page lets you move only one grade item or category at a time. It takes two page refreshes and two mouse clicks per move. This is very tedious and time-consuming when many items have to be moved around. MDL-13775 and MDL-12502 address this problem.&lt;br /&gt;
&lt;br /&gt;
===Removing the &#039;&#039;overridden&#039;&#039; attribute of individual grades===&lt;br /&gt;
When grades are imported into an existing gradebook, or when grades are manually edited in the grader report, these grades become [[Grade_editing#Overridden|overridden]], which prevents linked activity modules from updating this grade. Removing this attribute is very time-consuming, since one must enter the edit page of each individual grade, untick the checkbox and submit the form. This issue is discussed in [http://moodle.org/mod/forum/discuss.php?d=109636 this forum thread].&lt;br /&gt;
&lt;br /&gt;
===Viewing the overall contribution of each category and item to the course aggregation===&lt;br /&gt;
The tracker issue MDL-13777 reports the difficulty in seeing the contribution of each grade item and category to the course total. Calculations and weights may apply, which are not visible in any of the current reports except in each individual category or item&#039;s edit page.&lt;br /&gt;
&lt;br /&gt;
==Patch for Edit Categories and Items page==&lt;br /&gt;
Following is a proposed patch to the &#039;&#039;&#039;Edit categories and items&#039;&#039;&#039; page in the 1.9 gradebook. It addresses many usability issues while remaining simple. The following features are currently implemented:&lt;br /&gt;
&lt;br /&gt;
===Simple features===&lt;br /&gt;
[[Image:gradebook_categories_simple.png]]&lt;br /&gt;
*Items and categories are displayed in a table instead of a list. This improves readability and will make drag and drop easier to implement.&lt;br /&gt;
*All advanced features are hidden by default, so that the interface by default is almost identical to the original&lt;br /&gt;
*An additional column has checkboxes for grade items. All selected items can then be moved to one of the existing grade categories for the current course. Javascript &amp;quot;Select all/Select None&amp;quot; links replace these checkboxes for categories, to make it faster to select or de-select items.&lt;br /&gt;
&lt;br /&gt;
===Advanced features===&lt;br /&gt;
[[Image:gradebook_categories_advanced.png]]&lt;br /&gt;
*[[Category aggregation|Grade category aggregation type]]: changing this reloads the page, because the grade item weights/extra credits only apply to some of these aggregation types.&lt;br /&gt;
*Weight or extra credit: depending on the parent category&#039;s aggregation type, each category and grade item may have an input field or a checkbox for weight or extra credit. All these can be edited, then submitted with one click.&lt;br /&gt;
*Grade range is displayed for information purposes, but cannot be edited through this interface (it is most often controlled by the linked activity)&lt;br /&gt;
*[[Grade_categories#Aggregate_only_non-empty_grades|Aggregate only non-empty grades]]&lt;br /&gt;
*[[Grade_categories#Aggregate_including_sub-categories|Aggregate including sub-categories]]&lt;br /&gt;
*[[Grade_categories#Include_outcomes_in_aggregation|Include outcomes in aggregation]]&lt;br /&gt;
*[[Grade_categories#Drop_the_lowest|Drop the lowest]]&lt;br /&gt;
*[[Grade_categories#Keep_the_highest|Keep the highest]]&lt;br /&gt;
*[[Grade items|Multiplicator]]&lt;br /&gt;
*[[Grade_items|Offset]]&lt;br /&gt;
&lt;br /&gt;
All these settings can be changed then submitted with only one page reload. Just be mindful that changing a category&#039;s aggregation type will reload the page and you will lose any non-submitted changes you made to the other settings.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Grades_FAQ&amp;diff=46151</id>
		<title>Grades FAQ</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Grades_FAQ&amp;diff=46151"/>
		<updated>2008-11-04T10:37:03Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: I can&amp;#039;t find setting X in the grade category edit page! Where is it?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Grades}}&lt;br /&gt;
== General ==&lt;br /&gt;
&lt;br /&gt;
===How can I change how grades are displayed?===&lt;br /&gt;
&lt;br /&gt;
Grades may be displayed as as actual grades, as percentages (in reference to the minimum and maximum grades) or as letters.&lt;br /&gt;
&lt;br /&gt;
The default grade display type for the site is set by an administrator in &#039;&#039;Administration &amp;gt; Grades &amp;gt; [[Grade item settings]]&#039;&#039;. However, this may be changed at course level.&lt;br /&gt;
&lt;br /&gt;
To change how grades are displayed for particular [[Grade items|grade items]], or category and course summaries (called aggregations):&lt;br /&gt;
&lt;br /&gt;
# Follow the grades link in the course administration block.&lt;br /&gt;
# Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# Click the edit icon for the grade item, category total or course total.&lt;br /&gt;
# From the Grade display type menu, select real (for actual grades), percentage or letter.&lt;br /&gt;
# Click the &amp;quot;Save changes&amp;quot; button at the bottom of the page.&lt;br /&gt;
&lt;br /&gt;
Alternatively, to change how grades are displayed for the whole course:&lt;br /&gt;
&lt;br /&gt;
# Follow the grades link in the course administration block.&lt;br /&gt;
# Select &amp;quot;Course settings&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# From the Grade display type menu, select real (for actual grades), percentage or letter.&lt;br /&gt;
# Click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
===How can I hide entered grades until a specified date?===&lt;br /&gt;
&lt;br /&gt;
To set a &amp;quot;Hidden until&amp;quot; date:&lt;br /&gt;
&lt;br /&gt;
#Access the course gradebook via the grades link in the course administration block.&lt;br /&gt;
#Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
#Click on the edit icon opposite the activity for which a &amp;quot;Hidden until&amp;quot; date is to be set.&lt;br /&gt;
#On the edit grade item page, ensure that advanced settings are displayed. (Click the &amp;quot;Show advanced&amp;quot; button if not.)&lt;br /&gt;
#Enable the &amp;quot;Hidden until&amp;quot; setting by unchecking the disable checkbox, then set a date.&lt;br /&gt;
#Click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
=== Is it possible to show the teachers/administrators&#039; grades in the grader report? ===&lt;br /&gt;
Yes, at the site level you can define which roles will appear in the grader report. This can be found in [[General_grade_settings#Graded_Roles|Administration &amp;gt; Grades &amp;gt; General settings]]. Also read [http://moodle.org/mod/forum/discuss.php?d=92612 this discussion] for some more ideas.&lt;br /&gt;
&lt;br /&gt;
===Why can&#039;t I change a grade within an assignment after changing it in the gradebook?===&lt;br /&gt;
&lt;br /&gt;
When you edit a grade directly in the gradebook, an &amp;quot;overridden&amp;quot; flag is set, meaning that the grade can no longer be changed from within the assignment.&lt;br /&gt;
&lt;br /&gt;
However, the flag can be removed by turning editing on in the [[Grader report|grader report]], then clicking the [[Grade editing|edit grade]] icon, unchecking the overridden box and saving the changes.&lt;br /&gt;
&lt;br /&gt;
===How do I get groups to show up in the grader report?===&lt;br /&gt;
&lt;br /&gt;
For groups to show up in the grader report, group mode should be set to visible or separate groups in the [[Course settings|course settings]]. This will result in a groups dropdown menu being displayed, enabling a teacher to view the grades of all participants, or only the grades for a selected group.&lt;br /&gt;
&lt;br /&gt;
== Reports ==&lt;br /&gt;
=== How do I create my own custom gradebook reports? ===&lt;br /&gt;
Here is a [[Development:Gradebook_Report_Tutorial|tutorial]] explaining all the main steps involved.&lt;br /&gt;
&lt;br /&gt;
== Aggregation ==&lt;br /&gt;
=== I can&#039;t find where to change the aggregation type for my gradebook categories! ===&lt;br /&gt;
Each category has an aggregation type, which can be changed through that category&#039;s &amp;quot;edit&amp;quot; page. To access that page, you must use one of 2 ways:&lt;br /&gt;
&lt;br /&gt;
1. In the grader report, turn &amp;quot;Editing&amp;quot; on, then click the little &amp;quot;hand&amp;quot; icon next to the category whose aggregation you want to change&lt;br /&gt;
2. In the &amp;quot;Edit categories and Items&amp;quot; page (accessible through the &amp;quot;choose an action&amp;quot; menu, top left), you see a tree view of the categories and items in your gradebook. The top category is the course category. Each category also has a &amp;quot;hand&amp;quot; icon, which leads to the category edit page&lt;br /&gt;
&lt;br /&gt;
=== How can I grade some of my activities without the results affecting my students&#039; course total? ===&lt;br /&gt;
#Create two [[Grade categories]], one for your &amp;quot;activities still being graded,&amp;quot; and one for your &amp;quot;released&amp;quot; activities.&lt;br /&gt;
#Ensure that &amp;quot;Aggregate including subcategories&amp;quot; (an advanced option) is unchecked for your top level course grade category.&lt;br /&gt;
##Where is this?  In gradebook (grader report), in the upper right corner, click the &amp;quot;Turn Editing On&amp;quot; button.&lt;br /&gt;
##Click the edit icon next to the &amp;quot;course category&amp;quot; (usually your course name, just above the quiz names and below all the clickable links that were revealed when you turned editing on)&lt;br /&gt;
##Then make sure you have the &amp;quot;Show Advanced&amp;quot; option turned on.&lt;br /&gt;
#Edit the &amp;quot;activities still being graded&amp;quot; category&#039;s &amp;quot;course total&amp;quot; item. (This is one of the categories you created above.)&lt;br /&gt;
##Where is this?  Look for the edit icon under &amp;quot;category total&amp;quot; that is below this category&#039;s name&lt;br /&gt;
#Set the &amp;quot;grade type&amp;quot; to &amp;quot;none&amp;quot;.&lt;br /&gt;
#Tick the &amp;quot;Hidden&amp;quot; checkbox.&lt;br /&gt;
#Save your changes.&lt;br /&gt;
#Move all your activities being graded in the &amp;quot;activities still being graded&amp;quot;  category.&lt;br /&gt;
#Move all your activities already graded in the &amp;quot;released&amp;quot; category.&lt;br /&gt;
&lt;br /&gt;
Note: I rewrote this a bit, to help people find where things are.  However, this method didn&#039;t seem to work for me on Moodle 1.9.&lt;br /&gt;
&lt;br /&gt;
=== My student completed only one activity out of 5, but his course total shows 100%. How do I show a more &amp;quot;progressive&amp;quot; course total? ===&lt;br /&gt;
By default, only non-empty grades are aggregated, the others are ignored. However, you can change this setting as well as others that affect the course total, by turning &amp;quot;Editing&amp;quot; on in the grader report, and clicking the &amp;quot;Edit&amp;quot; icon next to the course category (the very top row of the grader report).&lt;br /&gt;
&lt;br /&gt;
You can untick the box &amp;quot;Aggregate only non-empty grades&amp;quot; if you want to show a more &amp;quot;progressive&amp;quot; score for each student. Their empty grades will count as a 0 and will be counted in the course mean/total.&lt;br /&gt;
 &lt;br /&gt;
If you prefer to show a sum of points, rather than a percentage, you can change the course category&#039;s aggregation method to &amp;quot;Sum of grades&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== How can I display the average grade for my course categories (not grade categories)? ===&lt;br /&gt;
In Moodle 1.9 there is no way to aggregate course totals within each category. The gradebook is course-centered, and there is currently no User Interface for showing grades within an entire course category at once.&lt;br /&gt;
&lt;br /&gt;
=== How can I setup weighted assignments? ===&lt;br /&gt;
See [[Using &amp;quot;Weighted Mean of Grades&amp;quot; to weight categories containing assignments]].&lt;br /&gt;
&lt;br /&gt;
== Categories ==&lt;br /&gt;
=== How many depths of categories/subcategories can I create? ===&lt;br /&gt;
There is no programmatic limit, but there are practical limits. Very deeply nested structures are difficult to manage. 3 levels of categories should be sufficient for most situations. Note that there is always at least one level of categories, since the Course category always encompasses all other categories and grade items, can cannot be deleted.&lt;br /&gt;
&lt;br /&gt;
=== I can&#039;t find setting X in the grade category edit page! Where is it? ===&lt;br /&gt;
If a setting documented on the [[Grade categories]] page does not appear on your edit page, it may mean that it is set globally in your site. See [[Grade_category_settings#Forcing_settings|Forcing settings]] for more information.&lt;br /&gt;
&lt;br /&gt;
== Outcomes ==&lt;br /&gt;
=== I have just upgraded to Moodle 1.9, and I want to set up an outcome item for my course. What are the steps required? ===&lt;br /&gt;
#[[General_grade_settings#Enable_outcomes|Administration &amp;gt; Grades &amp;gt; General settings &amp;gt; Enable outcomes]]&lt;br /&gt;
#[[Scales#Creating_a_new_scale|Create a scale]]&lt;br /&gt;
#Create a course outcome (read the [[Outcomes| outcomes documentation]] for instructions). Assign to it the scale you just created.&lt;br /&gt;
#Assign the outcome to your course&lt;br /&gt;
#Enter the &amp;quot;Grades&amp;quot; section of your course, from the course administration block&lt;br /&gt;
#In the Actions menu (top left), select Edit -&amp;gt; Categories and Items&lt;br /&gt;
#Click &amp;quot;Add outcome item&amp;quot;&lt;br /&gt;
#Follow the instructions of the [[Outcome items|outcome items documentation]] to create the outcome item&lt;br /&gt;
&lt;br /&gt;
You can now give your students a rating on the outcome dimension you just created. If you created a standard outcome, you will be able to use it in other courses and follow your students&#039; performance across these courses.&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
=== The activity module (Module name) doesn&#039;t support grading. How can I give my students a grade anyway? ===&lt;br /&gt;
You can create a [[Grade_items#Manual_grade_items|grade item]] manually in the gradebook. You will have to grade your students through the [[Grader report]] interface (in editing mode).&lt;br /&gt;
&lt;br /&gt;
=== I just graded some of my students using the (Module name) interface, but the results aren&#039;t showing up in the grader report. What&#039;s going on? ===&lt;br /&gt;
Here are some of the possible reasons:&lt;br /&gt;
&lt;br /&gt;
#The corresponding [[Grade items|grade item]] is [[Grade_locking#In_grade_items|locked]], or its parent [[Grade categories|category]] is [[Grade_locking#In_grade_categories|locked]].&lt;br /&gt;
#The module code is not using the [[Development:Grades#API_for_communication_with_modules.2Fblocks|gradebook API]] correctly&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[Gradebook 1.9 Tutorial]]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?id=2122 Gradebook forum]&lt;br /&gt;
&lt;br /&gt;
Using Moodle forum discussions:&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=102609 Can I aggregate only non hidden items?]&lt;br /&gt;
&lt;br /&gt;
[[Category:FAQ]]&lt;br /&gt;
&lt;br /&gt;
[[ca:PMF de les qualificacions]]&lt;br /&gt;
[[fr:FAQ des notes]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Grade_categories&amp;diff=46150</id>
		<title>Grade categories</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Grade_categories&amp;diff=46150"/>
		<updated>2008-11-04T10:34:38Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Link to Grade_category_settings#Forcing_settings&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Grades}}&lt;br /&gt;
[[Image:Edit grade category.png|thumb|Editing a grade category]]Grades can be organised into grade categories. &lt;br /&gt;
A grade category has its own aggregated grade which is calculated from its grade items. There is no limit to the level of nesting of categories (a category may belong to another category). However, each grade item may belong to only one category. Also, all grade items and categories belong to at least one, permanent category: [[Edit_categories_and_items#Top_category|the course category]].&lt;br /&gt;
&lt;br /&gt;
==Adding a grade category==&lt;br /&gt;
To add a grade category:&lt;br /&gt;
# Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# Click the &amp;quot;Add category&amp;quot; button near the bottom of the page.&lt;br /&gt;
# Give the grade category a meaningful name.&lt;br /&gt;
# Select grade category settings as appropriate. Advanced settings may be made available by clicking the &amp;quot;Show advanced&amp;quot; button.&lt;br /&gt;
# Click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
==Editing a grade category==&lt;br /&gt;
To edit a grade category:&lt;br /&gt;
# Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# Click the edit icon opposite the grade category you wish to edit.&lt;br /&gt;
# After editing the grade category, click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
==Settings==&lt;br /&gt;
If any of the following do not appear in your page, it may mean that they are set globally in your site. See [[Grade_category_settings#Forcing_settings|Forcing settings]] for more information.&lt;br /&gt;
&lt;br /&gt;
=== Aggregation ===&lt;br /&gt;
See [[Category aggregation]] for a detailed explanation.&lt;br /&gt;
&lt;br /&gt;
=== Aggregate only non-empty grades ===&lt;br /&gt;
Non-existent grades are either treated as minimal grades or not included in the aggregation. For example, an assignment graded between 0 and 100 for which only half the students have been graded will either count the non-graded submissions as 0 (option switched off) or will ignore them (option switched on).&lt;br /&gt;
&lt;br /&gt;
Important: An empty grade is simply a missing gradebook entry, and could mean different things. For example, it could be a participant who hasn&#039;t yet submitted an assignment, an assignment submission not yet graded by the teacher, or a grade that has been manually deleted by the gradebook administrator. Caution in interpreting these &amp;quot;empty grades&amp;quot; is thus advised.&lt;br /&gt;
&lt;br /&gt;
=== Aggregate including sub-categories ===&lt;br /&gt;
The aggregation is usually done only with immediate children, it is also possible to aggregate grades in all subcategories excluding other aggregated grades.&lt;br /&gt;
&lt;br /&gt;
=== Include outcomes in aggregation ===&lt;br /&gt;
Including outcomes in aggregation may not lead to the desired overall grade, so you have the option to include or leave them out.&lt;br /&gt;
&lt;br /&gt;
=== Drop the lowest ===&lt;br /&gt;
If set, this option will drop the X lowest grades, X being the selected value for this option.&lt;br /&gt;
&lt;br /&gt;
=== Keep the highest ===&lt;br /&gt;
If set, this option will only retain the X highest grades, X being the selected value for this option.&lt;br /&gt;
&lt;br /&gt;
=== Aggregation view ===&lt;br /&gt;
Each category can be displayed in three ways: Full mode (aggregated column and grade item columns), the aggregated column only, or the grade items alone.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
*[[Grade items]]&lt;br /&gt;
*[[Edit categories and items]]&lt;br /&gt;
*[[Grade category settings]] - for administrators&lt;br /&gt;
*[http://www.youtube.com/watch?v=sUslTuZPu6A Video showing the effects of the grade category settings]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/discuss.php?d=91632 Grade categories and weights 1.8 to 1.9?] forum discussion&lt;br /&gt;
&lt;br /&gt;
[[ca:grade/edit/tree/category]]&lt;br /&gt;
[[fr:Catégories d&#039;évaluation]]&lt;br /&gt;
[[cs:Kategorie známek]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Grades_FAQ&amp;diff=46149</id>
		<title>Grades FAQ</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Grades_FAQ&amp;diff=46149"/>
		<updated>2008-11-04T10:30:25Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: How can I setup weighted assignments?&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Grades}}&lt;br /&gt;
== General ==&lt;br /&gt;
&lt;br /&gt;
===How can I change how grades are displayed?===&lt;br /&gt;
&lt;br /&gt;
Grades may be displayed as as actual grades, as percentages (in reference to the minimum and maximum grades) or as letters.&lt;br /&gt;
&lt;br /&gt;
The default grade display type for the site is set by an administrator in &#039;&#039;Administration &amp;gt; Grades &amp;gt; [[Grade item settings]]&#039;&#039;. However, this may be changed at course level.&lt;br /&gt;
&lt;br /&gt;
To change how grades are displayed for particular [[Grade items|grade items]], or category and course summaries (called aggregations):&lt;br /&gt;
&lt;br /&gt;
# Follow the grades link in the course administration block.&lt;br /&gt;
# Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# Click the edit icon for the grade item, category total or course total.&lt;br /&gt;
# From the Grade display type menu, select real (for actual grades), percentage or letter.&lt;br /&gt;
# Click the &amp;quot;Save changes&amp;quot; button at the bottom of the page.&lt;br /&gt;
&lt;br /&gt;
Alternatively, to change how grades are displayed for the whole course:&lt;br /&gt;
&lt;br /&gt;
# Follow the grades link in the course administration block.&lt;br /&gt;
# Select &amp;quot;Course settings&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# From the Grade display type menu, select real (for actual grades), percentage or letter.&lt;br /&gt;
# Click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
===How can I hide entered grades until a specified date?===&lt;br /&gt;
&lt;br /&gt;
To set a &amp;quot;Hidden until&amp;quot; date:&lt;br /&gt;
&lt;br /&gt;
#Access the course gradebook via the grades link in the course administration block.&lt;br /&gt;
#Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
#Click on the edit icon opposite the activity for which a &amp;quot;Hidden until&amp;quot; date is to be set.&lt;br /&gt;
#On the edit grade item page, ensure that advanced settings are displayed. (Click the &amp;quot;Show advanced&amp;quot; button if not.)&lt;br /&gt;
#Enable the &amp;quot;Hidden until&amp;quot; setting by unchecking the disable checkbox, then set a date.&lt;br /&gt;
#Click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
=== Is it possible to show the teachers/administrators&#039; grades in the grader report? ===&lt;br /&gt;
Yes, at the site level you can define which roles will appear in the grader report. This can be found in [[General_grade_settings#Graded_Roles|Administration &amp;gt; Grades &amp;gt; General settings]]. Also read [http://moodle.org/mod/forum/discuss.php?d=92612 this discussion] for some more ideas.&lt;br /&gt;
&lt;br /&gt;
===Why can&#039;t I change a grade within an assignment after changing it in the gradebook?===&lt;br /&gt;
&lt;br /&gt;
When you edit a grade directly in the gradebook, an &amp;quot;overridden&amp;quot; flag is set, meaning that the grade can no longer be changed from within the assignment.&lt;br /&gt;
&lt;br /&gt;
However, the flag can be removed by turning editing on in the [[Grader report|grader report]], then clicking the [[Grade editing|edit grade]] icon, unchecking the overridden box and saving the changes.&lt;br /&gt;
&lt;br /&gt;
===How do I get groups to show up in the grader report?===&lt;br /&gt;
&lt;br /&gt;
For groups to show up in the grader report, group mode should be set to visible or separate groups in the [[Course settings|course settings]]. This will result in a groups dropdown menu being displayed, enabling a teacher to view the grades of all participants, or only the grades for a selected group.&lt;br /&gt;
&lt;br /&gt;
== Reports ==&lt;br /&gt;
=== How do I create my own custom gradebook reports? ===&lt;br /&gt;
Here is a [[Development:Gradebook_Report_Tutorial|tutorial]] explaining all the main steps involved.&lt;br /&gt;
&lt;br /&gt;
== Aggregation ==&lt;br /&gt;
=== I can&#039;t find where to change the aggregation type for my gradebook categories! ===&lt;br /&gt;
Each category has an aggregation type, which can be changed through that category&#039;s &amp;quot;edit&amp;quot; page. To access that page, you must use one of 2 ways:&lt;br /&gt;
&lt;br /&gt;
1. In the grader report, turn &amp;quot;Editing&amp;quot; on, then click the little &amp;quot;hand&amp;quot; icon next to the category whose aggregation you want to change&lt;br /&gt;
2. In the &amp;quot;Edit categories and Items&amp;quot; page (accessible through the &amp;quot;choose an action&amp;quot; menu, top left), you see a tree view of the categories and items in your gradebook. The top category is the course category. Each category also has a &amp;quot;hand&amp;quot; icon, which leads to the category edit page&lt;br /&gt;
&lt;br /&gt;
=== How can I grade some of my activities without the results affecting my students&#039; course total? ===&lt;br /&gt;
#Create two [[Grade categories]], one for your &amp;quot;activities still being graded,&amp;quot; and one for your &amp;quot;released&amp;quot; activities.&lt;br /&gt;
#Ensure that &amp;quot;Aggregate including subcategories&amp;quot; (an advanced option) is unchecked for your top level course grade category.&lt;br /&gt;
##Where is this?  In gradebook (grader report), in the upper right corner, click the &amp;quot;Turn Editing On&amp;quot; button.&lt;br /&gt;
##Click the edit icon next to the &amp;quot;course category&amp;quot; (usually your course name, just above the quiz names and below all the clickable links that were revealed when you turned editing on)&lt;br /&gt;
##Then make sure you have the &amp;quot;Show Advanced&amp;quot; option turned on.&lt;br /&gt;
#Edit the &amp;quot;activities still being graded&amp;quot; category&#039;s &amp;quot;course total&amp;quot; item. (This is one of the categories you created above.)&lt;br /&gt;
##Where is this?  Look for the edit icon under &amp;quot;category total&amp;quot; that is below this category&#039;s name&lt;br /&gt;
#Set the &amp;quot;grade type&amp;quot; to &amp;quot;none&amp;quot;.&lt;br /&gt;
#Tick the &amp;quot;Hidden&amp;quot; checkbox.&lt;br /&gt;
#Save your changes.&lt;br /&gt;
#Move all your activities being graded in the &amp;quot;activities still being graded&amp;quot;  category.&lt;br /&gt;
#Move all your activities already graded in the &amp;quot;released&amp;quot; category.&lt;br /&gt;
&lt;br /&gt;
Note: I rewrote this a bit, to help people find where things are.  However, this method didn&#039;t seem to work for me on Moodle 1.9.&lt;br /&gt;
&lt;br /&gt;
=== My student completed only one activity out of 5, but his course total shows 100%. How do I show a more &amp;quot;progressive&amp;quot; course total? ===&lt;br /&gt;
By default, only non-empty grades are aggregated, the others are ignored. However, you can change this setting as well as others that affect the course total, by turning &amp;quot;Editing&amp;quot; on in the grader report, and clicking the &amp;quot;Edit&amp;quot; icon next to the course category (the very top row of the grader report).&lt;br /&gt;
&lt;br /&gt;
You can untick the box &amp;quot;Aggregate only non-empty grades&amp;quot; if you want to show a more &amp;quot;progressive&amp;quot; score for each student. Their empty grades will count as a 0 and will be counted in the course mean/total.&lt;br /&gt;
 &lt;br /&gt;
If you prefer to show a sum of points, rather than a percentage, you can change the course category&#039;s aggregation method to &amp;quot;Sum of grades&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== How can I display the average grade for my course categories (not grade categories)? ===&lt;br /&gt;
In Moodle 1.9 there is no way to aggregate course totals within each category. The gradebook is course-centered, and there is currently no User Interface for showing grades within an entire course category at once.&lt;br /&gt;
&lt;br /&gt;
=== How can I setup weighted assignments? ===&lt;br /&gt;
See [[Using &amp;quot;Weighted Mean of Grades&amp;quot; to weight categories containing assignments]].&lt;br /&gt;
&lt;br /&gt;
== Categories ==&lt;br /&gt;
=== How many depths of categories/subcategories can I create? ===&lt;br /&gt;
There is no programmatic limit, but there are practical limits. Very deeply nested structures are difficult to manage. 3 levels of categories should be sufficient for most situations. Note that there is always at least one level of categories, since the Course category always encompasses all other categories and grade items, can cannot be deleted.&lt;br /&gt;
&lt;br /&gt;
== Outcomes ==&lt;br /&gt;
=== I have just upgraded to Moodle 1.9, and I want to set up an outcome item for my course. What are the steps required? ===&lt;br /&gt;
#[[General_grade_settings#Enable_outcomes|Administration &amp;gt; Grades &amp;gt; General settings &amp;gt; Enable outcomes]]&lt;br /&gt;
#[[Scales#Creating_a_new_scale|Create a scale]]&lt;br /&gt;
#Create a course outcome (read the [[Outcomes| outcomes documentation]] for instructions). Assign to it the scale you just created.&lt;br /&gt;
#Assign the outcome to your course&lt;br /&gt;
#Enter the &amp;quot;Grades&amp;quot; section of your course, from the course administration block&lt;br /&gt;
#In the Actions menu (top left), select Edit -&amp;gt; Categories and Items&lt;br /&gt;
#Click &amp;quot;Add outcome item&amp;quot;&lt;br /&gt;
#Follow the instructions of the [[Outcome items|outcome items documentation]] to create the outcome item&lt;br /&gt;
&lt;br /&gt;
You can now give your students a rating on the outcome dimension you just created. If you created a standard outcome, you will be able to use it in other courses and follow your students&#039; performance across these courses.&lt;br /&gt;
&lt;br /&gt;
== Modules ==&lt;br /&gt;
=== The activity module (Module name) doesn&#039;t support grading. How can I give my students a grade anyway? ===&lt;br /&gt;
You can create a [[Grade_items#Manual_grade_items|grade item]] manually in the gradebook. You will have to grade your students through the [[Grader report]] interface (in editing mode).&lt;br /&gt;
&lt;br /&gt;
=== I just graded some of my students using the (Module name) interface, but the results aren&#039;t showing up in the grader report. What&#039;s going on? ===&lt;br /&gt;
Here are some of the possible reasons:&lt;br /&gt;
&lt;br /&gt;
#The corresponding [[Grade items|grade item]] is [[Grade_locking#In_grade_items|locked]], or its parent [[Grade categories|category]] is [[Grade_locking#In_grade_categories|locked]].&lt;br /&gt;
#The module code is not using the [[Development:Grades#API_for_communication_with_modules.2Fblocks|gradebook API]] correctly&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
*[[Gradebook 1.9 Tutorial]]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?id=2122 Gradebook forum]&lt;br /&gt;
&lt;br /&gt;
Using Moodle forum discussions:&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=102609 Can I aggregate only non hidden items?]&lt;br /&gt;
&lt;br /&gt;
[[Category:FAQ]]&lt;br /&gt;
&lt;br /&gt;
[[ca:PMF de les qualificacions]]&lt;br /&gt;
[[fr:FAQ des notes]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Using_%22Weighted_Mean_of_Grades%22_to_weight_categories_containing_assignments&amp;diff=46148</id>
		<title>Using &quot;Weighted Mean of Grades&quot; to weight categories containing assignments</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Using_%22Weighted_Mean_of_Grades%22_to_weight_categories_containing_assignments&amp;diff=46148"/>
		<updated>2008-11-04T10:27:14Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Grades}}&lt;br /&gt;
The first feature of the new grade book that some may want to be able to master is the ability to &#039;clump&#039; assignments into categories and then have each category be assigned a different weight in the overall final grade. (Note - The non-technical term &#039;clump&#039; is being used to avoid the term aggregate in the hopes of preventing confusion given the terminology used in the new grade book)&lt;br /&gt;
&lt;br /&gt;
Example (how some courses could be evaluated using categories)&lt;br /&gt;
&lt;br /&gt;
Attendance &amp;amp; Participation 40%, &lt;br /&gt;
Quizzes 20%, &lt;br /&gt;
Tests (Mid-term &amp;amp; Final) 20%, &lt;br /&gt;
Final Projects 20%, &lt;br /&gt;
&lt;br /&gt;
First, create the categories to be used.  This can be done after assignments have been created but we found it easier to create the categories first. For this short doc two categories were utilized (Reading and Writing).&lt;br /&gt;
&lt;br /&gt;
In order to be able to weight categories, select &amp;quot;Weighted Mean of Grades&amp;quot; as the Aggregation method. You can also select the weight the category will be given here.  In this case Reading is being weighted at 10% and Writing at 90%.  These figures can be adjusted after creating the categories by selecting Categories and Items from the pulldown window from within the Grader.&lt;br /&gt;
&lt;br /&gt;
Here are the details of the two categories created.&lt;br /&gt;
&lt;br /&gt;
[[Image:Reading_10Percent.jpg]]&lt;br /&gt;
[[Image:Writing_90Percent.jpg]]&lt;br /&gt;
&lt;br /&gt;
For this short doc four Assignments (offline activities) were created.  Assign each a category at the bottom of the page when creating the assignment.  (they can be assigned later as well from the grade book but since the categories have already been created it is just as easy to assign the category when creating the assignment)&lt;br /&gt;
&lt;br /&gt;
[[Image:Giving_Assignment_A_Category.jpg]]  &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
(Note - We noticed some irregular behavior with the ID Number when leaving it blank and letting Moodle generate it automatically so we decided it is better to assign an ID number here. 1234 was used for simplicity in this example.)&lt;br /&gt;
&lt;br /&gt;
So after creating the 4 assignments this is what it should look like (roughly) from the front page of your course.&lt;br /&gt;
&lt;br /&gt;
[[Image:Four_assignments.jpg]]&lt;br /&gt;
&lt;br /&gt;
After grading the assignments you should be able to view the grade book and see that the categories are being weighted as desired in the overall Aggregation Course Total.&lt;br /&gt;
&lt;br /&gt;
[[Image:WeightedMeanGradeReport2.jpg]]&lt;br /&gt;
&lt;br /&gt;
Explanation for the student at the top of the list - Reading is 10% thus the 100% score in the reading category results in a 10% contribution to the overall Aggregation Course Total and Writing is set at 90% thus the 10% score results in a 9% contribution to the overall Aggregation Course Total.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Grade_categories&amp;diff=46147</id>
		<title>Grade categories</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Grade_categories&amp;diff=46147"/>
		<updated>2008-11-04T10:17:28Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Settings */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Grades}}&lt;br /&gt;
[[Image:Edit grade category.png|thumb|Editing a grade category]]Grades can be organised into grade categories. &lt;br /&gt;
A grade category has its own aggregated grade which is calculated from its grade items. There is no limit to the level of nesting of categories (a category may belong to another category). However, each grade item may belong to only one category. Also, all grade items and categories belong to at least one, permanent category: [[Edit_categories_and_items#Top_category|the course category]].&lt;br /&gt;
&lt;br /&gt;
==Adding a grade category==&lt;br /&gt;
To add a grade category:&lt;br /&gt;
# Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# Click the &amp;quot;Add category&amp;quot; button near the bottom of the page.&lt;br /&gt;
# Give the grade category a meaningful name.&lt;br /&gt;
# Select grade category settings as appropriate. Advanced settings may be made available by clicking the &amp;quot;Show advanced&amp;quot; button.&lt;br /&gt;
# Click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
==Editing a grade category==&lt;br /&gt;
To edit a grade category:&lt;br /&gt;
# Select &amp;quot;Categories and items&amp;quot; from the gradebook dropdown menu.&lt;br /&gt;
# Click the edit icon opposite the grade category you wish to edit.&lt;br /&gt;
# After editing the grade category, click the &amp;quot;Save changes&amp;quot; button.&lt;br /&gt;
&lt;br /&gt;
==Settings==&lt;br /&gt;
=== Aggregation ===&lt;br /&gt;
See [[Category aggregation]] for a detailed explanation.&lt;br /&gt;
&lt;br /&gt;
=== Aggregate only non-empty grades ===&lt;br /&gt;
Non-existent grades are either treated as minimal grades or not included in the aggregation. For example, an assignment graded between 0 and 100 for which only half the students have been graded will either count the non-graded submissions as 0 (option switched off) or will ignore them (option switched on).&lt;br /&gt;
&lt;br /&gt;
Important: An empty grade is simply a missing gradebook entry, and could mean different things. For example, it could be a participant who hasn&#039;t yet submitted an assignment, an assignment submission not yet graded by the teacher, or a grade that has been manually deleted by the gradebook administrator. Caution in interpreting these &amp;quot;empty grades&amp;quot; is thus advised.&lt;br /&gt;
&lt;br /&gt;
=== Aggregate including sub-categories ===&lt;br /&gt;
The aggregation is usually done only with immediate children, it is also possible to aggregate grades in all subcategories excluding other aggregated grades.&lt;br /&gt;
&lt;br /&gt;
=== Include outcomes in aggregation ===&lt;br /&gt;
Including outcomes in aggregation may not lead to the desired overall grade, so you have the option to include or leave them out.&lt;br /&gt;
&lt;br /&gt;
=== Drop the lowest ===&lt;br /&gt;
If set, this option will drop the X lowest grades, X being the selected value for this option.&lt;br /&gt;
&lt;br /&gt;
=== Keep the highest ===&lt;br /&gt;
If set, this option will only retain the X highest grades, X being the selected value for this option.&lt;br /&gt;
&lt;br /&gt;
=== Aggregation view ===&lt;br /&gt;
Each category can be displayed in three ways: Full mode (aggregated column and grade item columns), the aggregated column only, or the grade items alone.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
*[[Grade items]]&lt;br /&gt;
*[[Edit categories and items]]&lt;br /&gt;
*[[Grade category settings]] - for administrators&lt;br /&gt;
*[http://www.youtube.com/watch?v=sUslTuZPu6A Video showing the effects of the grade category settings]&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/discuss.php?d=91632 Grade categories and weights 1.8 to 1.9?] forum discussion&lt;br /&gt;
&lt;br /&gt;
[[ca:grade/edit/tree/category]]&lt;br /&gt;
[[fr:Catégories d&#039;évaluation]]&lt;br /&gt;
[[cs:Kategorie známek]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46145</id>
		<title>Development:Gradebook interface improvements for Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46145"/>
		<updated>2008-11-04T09:25:39Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
One of the goals of Moodle 2.0 is to improve the &#039;&#039;&#039;usability&#039;&#039;&#039; of the gradebook introduced in 1.9.&lt;br /&gt;
&lt;br /&gt;
The purpose of this page is to present a list of proposed improvements, and gather feedback on these implementations before they make it into this important release. These proposed improvements are based on bug reports and forum posts that have been coming in since the 1.9 release.&lt;br /&gt;
&lt;br /&gt;
We would really like your feedback on:&lt;br /&gt;
# the changes already proposed here&lt;br /&gt;
# any issues that you feel are not yet addressed &lt;br /&gt;
&lt;br /&gt;
Please join the discussions and post your suggestions in the [http://moodle.org/mod/forum/view.php?f=397 Gradebook forum].&lt;br /&gt;
&lt;br /&gt;
==Common frustrations==&lt;br /&gt;
Below are some of the most often reported usability issues in the 1.9 gradebook, which we are trying to address. Several of these are grouped as sub-tasks under MDL-16913.&lt;br /&gt;
&lt;br /&gt;
===Assigning weights to categories and grade items===&lt;br /&gt;
This is the most common frustration. Weights are used extensively in many institutions, and were used in 1.8. However, they are difficult to understand in 1.9, and difficult to set up. This issue is discussed extensively in MDL-15680.&lt;br /&gt;
&lt;br /&gt;
===Moving items to categories===&lt;br /&gt;
Currently, the [[Edit categories and items]] page lets you move only one grade item or category at a time. It takes two page refreshes and two mouse clicks per move. This is very tedious and time-consuming when many items have to be moved around. MDL-13775 and MDL-12502 address this problem.&lt;br /&gt;
&lt;br /&gt;
===Removing the &#039;&#039;overridden&#039;&#039; attribute of individual grades===&lt;br /&gt;
When grades are imported into an existing gradebook, or when grades are manually edited in the grader report, these grades become [[Grade_editing#Overridden|overridden]], which prevents linked activity modules from updating this grade. Removing this attribute is very time-consuming, since one must enter the edit page of each individual grade, untick the checkbox and submit the form. This issue is discussed in [http://moodle.org/mod/forum/discuss.php?d=109636 this forum thread].&lt;br /&gt;
&lt;br /&gt;
===Viewing the overall contribution of each category and item to the course aggregation===&lt;br /&gt;
The tracker issue MDL-13777 reports the difficulty in seeing the contribution of each grade item and category to the course total. Calculations and weights may apply, which are not visible in any of the current reports except in each individual category or item&#039;s edit page.&lt;br /&gt;
&lt;br /&gt;
==Patch for Edit Categories and Items page==&lt;br /&gt;
Following is a proposed patch to the &#039;&#039;&#039;Edit categories and items&#039;&#039;&#039; page in the 1.9 gradebook. It addresses many usability issues while remaining simple&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46144</id>
		<title>Development:Gradebook interface improvements for Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46144"/>
		<updated>2008-11-04T09:05:44Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
One of the goals of Moodle 2.0 is to improve the &#039;&#039;&#039;usability&#039;&#039;&#039; of the gradebook introduced in 1.9.&lt;br /&gt;
&lt;br /&gt;
The purpose of this page is to present a list of proposed improvements, and gather feedback on these implementations before they make it into this important release. These proposed improvements are based on bug reports and forum posts that have been coming in since the 1.9 release.&lt;br /&gt;
&lt;br /&gt;
We would really like your feedback on:&lt;br /&gt;
# the changes already proposed here&lt;br /&gt;
# any issues that you feel are not yet addressed &lt;br /&gt;
&lt;br /&gt;
Please join the discussions and post your suggestions in the [http://moodle.org/mod/forum/view.php?f=397 Gradebook forum].&lt;br /&gt;
&lt;br /&gt;
==Common frustrations==&lt;br /&gt;
Below are some of the most often reported usability issues in the 1.9 gradebook, which we are trying to address. Several of these are grouped as sub-tasks under MDL-16913.&lt;br /&gt;
&lt;br /&gt;
===Assigning weights to categories and grade items===&lt;br /&gt;
This is the most common frustration. Weights are used extensively in many institutions, and were used in 1.8. However, they are difficult to understand in 1.9, and difficult to set up.&lt;br /&gt;
&lt;br /&gt;
===Moving items to categories===&lt;br /&gt;
Currently, the [[Edit categories and items]] page lets you move only one grade item or category at a time. It takes two page refreshes and two mouse clicks per move. This is very tedious and time-consuming when many items have to be moved around.&lt;br /&gt;
&lt;br /&gt;
===Removing the &#039;&#039;overridden&#039;&#039; attribute of individual grades===&lt;br /&gt;
When grades are imported into an existing gradebook, or when grades are manually edited in the grader report, these grades become [[Grade_editing#Overridden|overridden]], which prevents linked activity modules from updating this grade. Removing this attribute is very time-consuming, since one must enter the edit page of each individual grade, untick the checkbox and submit the form.&lt;br /&gt;
&lt;br /&gt;
===Viewing the overall contribution of each category and item to the course aggregation===&lt;br /&gt;
The tracker issue MDL-13777 reports the difficulty in seeing the contribution of each grade item and category to the course total. Calculations and weights may apply, which are not visible in any of the current reports except in each individual category or item&#039;s edit page.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46143</id>
		<title>Development:Gradebook interface improvements for Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46143"/>
		<updated>2008-11-04T09:05:03Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: common frustrations&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
One of the goals of Moodle 2.0 is to improve the &#039;&#039;&#039;usability&#039;&#039;&#039; of the gradebook introduced in 1.9.&lt;br /&gt;
&lt;br /&gt;
The purpose of this page is to present a list of proposed improvements, and gather feedback on these implementations before they make it into this important release. These proposed improvements are based on bug reports and forum posts that have been coming in since the 1.9 release.&lt;br /&gt;
&lt;br /&gt;
We would really like your feedback on:&lt;br /&gt;
# the changes already proposed here&lt;br /&gt;
# any issues that you feel are not yet addressed &lt;br /&gt;
&lt;br /&gt;
Please join the discussions and post your suggestions in the [http://moodle.org/mod/forum/view.php?f=397 Gradebook forum].&lt;br /&gt;
&lt;br /&gt;
==Common frustrations==&lt;br /&gt;
Below are some of the most often reported usability issues in the 1.9 gradebook, which we are trying to address.&lt;br /&gt;
&lt;br /&gt;
===Assigning weights to categories and grade items===&lt;br /&gt;
This is the most common frustration. Weights are used extensively in many institutions, and were used in 1.8. However, they are difficult to understand in 1.9, and difficult to set up.&lt;br /&gt;
&lt;br /&gt;
===Moving items to categories===&lt;br /&gt;
Currently, the [[Edit categories and items]] page lets you move only one grade item or category at a time. It takes two page refreshes and two mouse clicks per move. This is very tedious and time-consuming when many items have to be moved around.&lt;br /&gt;
&lt;br /&gt;
===Removing the &#039;&#039;overridden&#039;&#039; attribute of individual grades===&lt;br /&gt;
When grades are imported into an existing gradebook, or when grades are manually edited in the grader report, these grades become [[Grade_editing#Overridden|overridden]], which prevents linked activity modules from updating this grade. Removing this attribute is very time-consuming, since one must enter the edit page of each individual grade, untick the checkbox and submit the form.&lt;br /&gt;
&lt;br /&gt;
===Viewing the overall contribution of each category and item to the course aggregation===&lt;br /&gt;
The tracker issue MDL-13777 reports the difficulty in seeing the contribution of each grade item and category to the course total. Calculations and weights may apply, which are not visible in any of the current reports except in each individual category or item&#039;s edit page.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46142</id>
		<title>Development:Gradebook interface improvements for Moodle 2.0</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Gradebook_interface_improvements_for_Moodle_2.0&amp;diff=46142"/>
		<updated>2008-11-04T08:17:33Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Introduction&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
One of the goals of Moodle 2.0 is to improve the &#039;&#039;&#039;usability&#039;&#039;&#039; of the gradebook introduced in 1.9.&lt;br /&gt;
&lt;br /&gt;
The purpose of this page is to present a list of proposed improvements, and gather feedback on these implementations before they make it into this important release. These proposed improvements are based on bug reports and forum posts that have been coming in since the 1.9 release.&lt;br /&gt;
&lt;br /&gt;
We would really like your feedback on:&lt;br /&gt;
# the changes already proposed here&lt;br /&gt;
# any issues that you feel are not yet addressed &lt;br /&gt;
&lt;br /&gt;
Please join the discussions and post your suggestions in the [http://moodle.org/mod/forum/view.php?f=397 Gradebook forum].&lt;br /&gt;
&lt;br /&gt;
Most of these improvements will be backported to 1.9 before 2.0 is released. For this reason, we are developing it as a patch in contrib, which you can find in the [http://cvs.moodle.org/contrib/patches/grade/edit/tree/?pathrev=MOODLE_19_STABLE MOODLE_19_STABLE branch of contrib].&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Grades&amp;diff=45245</id>
		<title>Grades</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Grades&amp;diff=45245"/>
		<updated>2008-10-13T05:49:55Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Reverted edits by Koltyn (Talk); changed back to last version by Tsala&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Grades}}&amp;lt;p class=&amp;quot;note&amp;quot;&amp;gt;&#039;&#039;&#039;Note:&#039;&#039;&#039; This page, together with the pages listed in the block on the right, describe the gradebook in Moodle 1.9 onwards. For documentation on the gradebook in Moodle prior to 1.9, see [[Grades pre-1.9]].&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Introduction==&lt;br /&gt;
&lt;br /&gt;
The concepts of &#039;&#039;grades&#039;&#039; and of &#039;&#039;gradebook&#039;&#039; have been completely revisited in Moodle 1.9. Although these words are used in earlier versions, important differences are documented here in order to avoid misconceptions.&lt;br /&gt;
&lt;br /&gt;
The two central ideas of grading in Moodle 1.9 are:&lt;br /&gt;
&lt;br /&gt;
#&#039;&#039;&#039;Grades&#039;&#039;&#039; are scores attributed to participants in a Moodle course&lt;br /&gt;
#The &#039;&#039;&#039;gradebook&#039;&#039;&#039; is a repository of these grades: modules push their grades to it, but the gradebook doesn&#039;t push anything back to the modules&lt;br /&gt;
&lt;br /&gt;
The three building blocks of the Gradebook in Moodle 1.9 are&lt;br /&gt;
&lt;br /&gt;
*The [[Grade_categories|grade category]]&lt;br /&gt;
*The [[Grade_items|grade item]]&lt;br /&gt;
*The grade (see above)&lt;br /&gt;
&lt;br /&gt;
As an overview:&lt;br /&gt;
*A grade category groups grade items together, and has settings for affecting these grade items&lt;br /&gt;
*A grade item stores a grade for each course participant, and has settings for affecting these grades&lt;br /&gt;
*A grade has settings for affecting how it is displayed to the users, as well as [[Grade locking|locking]] and [[Grade hiding|hiding]] functions.&lt;br /&gt;
&lt;br /&gt;
Grades can be [[Grade_calculations|calculated]], [[Grade_categories#Aggregation|aggregated]] and [[Grader_report#Display|displayed]] in a variety of ways, the many settings having been designed to suit the needs of a great variety of organisations.&lt;br /&gt;
&lt;br /&gt;
Many activities in Moodle, such as [[Assignment module|assignments]], [[Forum module|forums]] and [[Quiz module|quizzes]] may be given grades. Grades may have numerical values, or words/phrases from a [[Scales|scale or rating system]].&lt;br /&gt;
&lt;br /&gt;
Grades can also be used as [[Outcomes|outcomes]] and as arbitrary text attributed to each participant in a course.&lt;br /&gt;
&lt;br /&gt;
==Grades pushed by modules==&lt;br /&gt;
When activity modules produce grades, they use the [[Development:Grades#API_for_communication_with_modules.2Fblocks|gradebook public API]] to push (or send) their grades to the gradebook. These grades are then stored in database tables that are independent of the modules. The grades are still kept in the module database tables, and the gradebook will never access or modify these original grades. &lt;br /&gt;
&lt;br /&gt;
The gradebook, however, provides administrators and teachers with tools for changing the ways in which grades are calculated, aggregated and displayed, as well as [[grade/edit/tree/grade|means to change the grades manually]] (a manual edit of a grade automatically locks the grade in the gradebook, so that the module which originally created the grade can no longer update that grade in the gradebook until the grade is unlocked).&lt;br /&gt;
&lt;br /&gt;
==Settings affecting grades==&lt;br /&gt;
Being the smallest unit in the gradebook, the grade is affected by many settings at different levels. Here is a list of these levels, in hierarchical order:&lt;br /&gt;
&lt;br /&gt;
*[[General_grade_settings|Site-wide general settings]]&lt;br /&gt;
*[[Grade_category_settings|Site-wide grade category settings]]&lt;br /&gt;
*[[Grade_item_settings|Site-wide grade item settings]]&lt;br /&gt;
*[[Gradebook_report_settings|Gradebook report settings]]&lt;br /&gt;
*[[Gradebook_course_settings|Course settings]]&lt;br /&gt;
*[[Grade_categories|Category settings]]&lt;br /&gt;
*[[Grade_items|Grade item settings]]&lt;br /&gt;
*[[grade/edit/tree/grade|Grade settings]]&lt;br /&gt;
&lt;br /&gt;
==Outcomes==&lt;br /&gt;
&lt;br /&gt;
[[Outcomes]] are specific descriptions of what a student is expected to be able to do or understand at the completion of an activity or course. An activity might have more than one outcome, and each may have a grade against it (usually on a [[Scales|scale]]).&lt;br /&gt;
&lt;br /&gt;
==Gradebook reports==&lt;br /&gt;
&lt;br /&gt;
The gradebook includes a variety of reports, available via the grades link in each [[Course administration block|course administration block]]:&lt;br /&gt;
&lt;br /&gt;
* [[Grader report]] - The main teacher view of a course gradebook. The &amp;quot;[[Grade preferences|My report preferences]]&amp;quot; tab in the grader report enables teachers to change how the grader report is displayed.&lt;br /&gt;
* [[Outcomes report]]&lt;br /&gt;
* [[Overview report]]&lt;br /&gt;
* [[User report]]&lt;br /&gt;
&lt;br /&gt;
==Grades organisation==&lt;br /&gt;
&lt;br /&gt;
Teachers may organise grades into [[Grade categories|grade categories]], [[Grade import|import]] and/or [[Grade export|export]] grades, and make [[Grade calculations|grade calculations]].&lt;br /&gt;
&lt;br /&gt;
Symbols to represent ranges of grades may be set as [[Grade letters|grade letters]].&lt;br /&gt;
&lt;br /&gt;
Administrators may control the appearance of the gradebook site-wide by adjusting settings available via the grades link in the site administration block:&lt;br /&gt;
&lt;br /&gt;
*[[General grade settings]]&lt;br /&gt;
*[[Grade category settings]]&lt;br /&gt;
*[[Grade item settings]]&lt;br /&gt;
*[[Gradebook report settings]] &lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
*Using Moodle [http://moodle.org/mod/forum/view.php?id=2122 Gradebook forum]&lt;br /&gt;
&lt;br /&gt;
Video tutorials:&lt;br /&gt;
*[http://www.youtube.com/watch?v=YeUy-_kbvqQ Basic Moodle Gradebook howto]&lt;br /&gt;
*[http://www.youtube.com/watch?v=5hrLNbifiGQ Gradebook reports]&lt;br /&gt;
*[http://www.youtube.com/watch?v=lXEefYe3qdk How to use the grade item settings and grade letters at admin level]&lt;br /&gt;
*[http://www.youtube.com/watch?v=sUslTuZPu6A Grade category settings]&lt;br /&gt;
*[http://www.youtube.com/watch?v=EB58W3KePBc How to set up the gradebook]&lt;br /&gt;
*[http://www.youtube.com/watch?v=PmkEGfvjj9U How to use outcomes in Moodle]&lt;br /&gt;
*[http://www.youtube.com/watch?v=yZcbN_7p2zI How to export grades from the gradebook]&lt;br /&gt;
*[http://www.youtube.com/watch?v=p6zWwJGb9TA How to use gradebook site settings and defaults]&lt;br /&gt;
*[http://www.youtube.com/watch?v=WKUGyzAXcyA How to set up calculations in the gradebook (basic)]&lt;br /&gt;
*[http://www.youtube.com/watch?v=VBEj8mmu8lM How to set up calculations in the gradebook (advanced)]&lt;br /&gt;
*[http://www.youtube.com/watch?v=jWPUEqdhI4A How to change the display of grades in the gradebook]&lt;br /&gt;
&lt;br /&gt;
[[ca:Qualificacions]]&lt;br /&gt;
[[cs:Známky]]&lt;br /&gt;
[[eu:Kalifikazioak]]&lt;br /&gt;
[[fr:Notes]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Calculated_question_type&amp;diff=45114</id>
		<title>Development:Calculated question type</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Calculated_question_type&amp;diff=45114"/>
		<updated>2008-10-10T14:25:52Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Flagging this article as Obsolete design&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{obsolete_design}}&lt;br /&gt;
{{Questiontype developer docs}}&lt;br /&gt;
{{Calculated question dev docs}}&lt;br /&gt;
The person who knows most about this question type is probably [http://moodle.org/user/view.php?id=112682&amp;amp;course=5 Pierre Pichet].&lt;br /&gt;
&lt;br /&gt;
==Database tables==&lt;br /&gt;
&lt;br /&gt;
===quiz_calculated===&lt;br /&gt;
The &#039;&#039;&#039;quiz_calculated&#039;&#039;&#039; table is an extension to the quiz_questions table by the calculated questiontype. However, it would be more suitable to change that to be an extension of the quiz_answers table, which, from a data perspective, is already possible, since an answer id is stored in the answer field. The questiontype code would need some changes to take this into account, however.&lt;br /&gt;
&lt;br /&gt;
;id :int(10) unsigned NOT NULL auto_increment,&lt;br /&gt;
;question :int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
;answer :int(10) unsigned NOT NULL default &#039;0&#039;,&lt;br /&gt;
;tolerance :varchar(20) NOT NULL default &#039;0.0&#039;,&lt;br /&gt;
;tolerancetype :int(10) NOT NULL default &#039;1&#039;,&lt;br /&gt;
;correctanswerlength :int(10) NOT NULL default &#039;2&#039;,&lt;br /&gt;
;correctanswerformat :int(10) NOT NULL default &#039;2&#039;,&lt;br /&gt;
&lt;br /&gt;
===quiz_numerical_units===&lt;br /&gt;
The calculated question type shares the [[Numerical_question_developer_docs#quiz_numerical_units|quiz_numerical_units table]] with the [[Numerical_question_developer_docs|numerical question type]].&lt;br /&gt;
&lt;br /&gt;
==Response storage==&lt;br /&gt;
The calculated questiontype inherits its methods create_session_and_responses(), restore_session_and_responses() and save_session_and_responses() from the abstract dataset dependent questiontype. This questiontype serializes the information of the chosen dataset together with the (single) response value into a string of the format datasetXX-RESPONSE, where dataset is the actual string &amp;quot;dataset&amp;quot;, XX is the number of the chosen dataset item and RESPONSE is the value that was received from the submitted form.&lt;br /&gt;
&lt;br /&gt;
==Question-&amp;gt;options==&lt;br /&gt;
&lt;br /&gt;
==State-&amp;gt;options==&lt;br /&gt;
==See also==&lt;br /&gt;
*[[Calculated question creation developper docs]]&lt;br /&gt;
*[[Calculated question development|Developer notes on calculated question development]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer|Calculated question type]]&lt;br /&gt;
[[Category:Quiz]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Student_projects/Secure_RSS_feeds&amp;diff=45113</id>
		<title>Student projects/Secure RSS feeds</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Student_projects/Secure_RSS_feeds&amp;diff=45113"/>
		<updated>2008-10-10T14:23:40Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{obsolete_design}}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;p class=&amp;quot;note&amp;quot;&amp;gt;&#039;&#039;&#039;Note&#039;&#039;&#039;: This page outlines ideas for the &amp;quot;Secure RSS feeds&amp;quot; project. If you have any comments or suggestions, please add them to the [[Talk:Student projects/Secure RSS Feeds|page comments]].&#039;&#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
== Status == &lt;br /&gt;
&lt;br /&gt;
Release candidate. All main objectives complete. &lt;br /&gt;
&lt;br /&gt;
==Summary==&lt;br /&gt;
Secure RSS feeds is a project about making the RSS feeds published by Moodle secure so that only desired people can access the feeds. More details here.[http://code.google.com/soc/2008/moodle/appinfo.html?csaid=3141B6C0C1823EA1]&lt;br /&gt;
&lt;br /&gt;
Typical RSS URL will look like: “&amp;lt;nowiki&amp;gt;http://domain/moodle/rss/file.php/contextid/&amp;lt;/nowiki&amp;gt;&amp;lt;u&amp;gt;[[#hashkey|hash_key]]&amp;lt;/u&amp;gt;user_id/modulename/instance/any/other/params/module/wants/rss.xml”. &lt;br /&gt;
&lt;br /&gt;
Where [[#hashkey|hash_key]] – special hash-string used to identify user.&lt;br /&gt;
&lt;br /&gt;
User is identified by comparing part [[#hashkey|hash_key]] with the real hash value of user_id + user_private_key(from DB) +  modulename + instance(from URL) concatenation. &lt;br /&gt;
&lt;br /&gt;
If someone stole one private feed URL, he won’t be able to use it for reading other private feeds.&lt;br /&gt;
&lt;br /&gt;
==Security==&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;div id=&amp;quot;hashkey&amp;quot;&amp;gt; Hash-key is a hash value from user_id, user_private_key, modulename (and other information, which is used to identify RSS feed) concatenation.  &amp;lt;/div&amp;gt;&lt;br /&gt;
# If hash-key is not specified, consider user as guest. &lt;br /&gt;
# In current version of spec, hashes are not additionally salted. &lt;br /&gt;
# User private keys are tied to context id&#039;s.&lt;br /&gt;
# There is an option to force https:// for all RSS feeds&lt;br /&gt;
&lt;br /&gt;
==Core functions==&lt;br /&gt;
&lt;br /&gt;
===rss_auth()===&lt;br /&gt;
&#039;&#039;&#039;rss_auth($hash_key, $user_id, $course_id, $context_id, $module, $instance, $info )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;$hash_key&#039;&#039; - long hash-like string from URL.&lt;br /&gt;
* &#039;&#039;$user_id&#039;&#039; - user id from URL&lt;br /&gt;
* &#039;&#039;$course_id&#039;&#039; - the id of the course this feeds belongs to&lt;br /&gt;
* &#039;&#039;$context_id&#039;&#039; - the id of the context this feeds belongs to&lt;br /&gt;
* &#039;&#039;$module&#039;&#039; -  module name or course module object this feeds belongs to&lt;br /&gt;
* &#039;&#039;$instance&#039;&#039; - instance id. Could be blogid, forumid etc&lt;br /&gt;
* &#039;&#039;$info&#039;&#039; - additonal information, which is used to accurately identify RSS feed. Can be array.&lt;br /&gt;
&lt;br /&gt;
Authenticates user by hash-string in URL, sets up $USER and other necessary stuff(done by calling Moodle core function require_user_key_login()). Checks if the user can access particular course and module.&lt;br /&gt;
Function terminates with error if user doesn&#039;t have access to course\module.&lt;br /&gt;
&lt;br /&gt;
===rss_get_url_key()===&lt;br /&gt;
&#039;&#039;&#039;rss_get_url_key( $userid, $contextid, $modulename, $instance, $info)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;$user&#039;&#039; - user id. &lt;br /&gt;
* &#039;&#039;$contextid&#039;&#039; - the id of the context this feeds belongs to&lt;br /&gt;
* &#039;&#039;$modulename&#039;&#039; -  module name this feeds belongs to&lt;br /&gt;
* &#039;&#039;$instance&#039;&#039; - instance id. Could be blogid, forumid etc&lt;br /&gt;
* &#039;&#039;$info&#039;&#039; - additonal information, which is used to accurately identify RSS feed. Can be array.&lt;br /&gt;
&lt;br /&gt;
Function returns long hash-like string, which can be used later to access specific RSS feed. Used when printing links.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===RSS feed generation===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;modulename_rss_newstuff($instance, $time,&amp;amp;$cache, $info)&#039;&#039;&#039;&lt;br /&gt;
This function checks if there is something new in module since $time&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;modulename_rss_generate_feed($instance, $context, $info, $cache)&#039;&#039;&#039;&lt;br /&gt;
This function generates and returns XML rss contents&lt;br /&gt;
&lt;br /&gt;
==Changes in RSS feed subsystem==&lt;br /&gt;
* No more Cron jobs for RSS feeds. &lt;br /&gt;
* All feeds are generated on the fly (i.e. no cached .xml files)&lt;br /&gt;
&lt;br /&gt;
Most of the times nothing changes in the feed - we do not have to send the actual feed content, we can just send HTTP 304 Not Modified header. And because no actual content is sent, this allows us to skip loading all capabilities, identifying users etc - improve performance. &lt;br /&gt;
&lt;br /&gt;
It may be convenient to prefetch some data in rss_newstuff(), that&#039;s why $cache is used. &lt;br /&gt;
However, it duplicates rcache functionality a bit, so I&#039;m thinking about removing it. &lt;br /&gt;
&lt;br /&gt;
But there is a problem, when rss_new_stuff() result depends on what capabilities user has. &lt;br /&gt;
In this situation during newstuff() check we assume that user has all the necessary capabilites. If there are no changes since last feed fetching - send 304 Not Modified. Otherwise, do the real check during feed content generation.&lt;br /&gt;
&lt;br /&gt;
==Database tables==&lt;br /&gt;
Fields added to existing tables.&lt;br /&gt;
&lt;br /&gt;
===course===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|rsstype&lt;br /&gt;
|int(1)&lt;br /&gt;
|0&lt;br /&gt;
|0 - disabled. 1 - recent activity rss&lt;br /&gt;
|-&lt;br /&gt;
|rssarticles&lt;br /&gt;
|int(2)&lt;br /&gt;
|0&lt;br /&gt;
|number of recent articles in RSS feed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===assigment===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|rsstype&lt;br /&gt;
|int(1)&lt;br /&gt;
|0&lt;br /&gt;
|0 - disabled. 1 - assignment submissions rss&lt;br /&gt;
|-&lt;br /&gt;
|rssarticles&lt;br /&gt;
|int(2)&lt;br /&gt;
|0&lt;br /&gt;
|number of recent articles in RSS feed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Interface mockups==&lt;br /&gt;
&lt;br /&gt;
===RSS links on Course page===&lt;br /&gt;
[[Image:blocks.gif]]&lt;br /&gt;
&lt;br /&gt;
===Calendar RSS links===&lt;br /&gt;
[[Image:calendar_rss.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:calblockrss.gif]]&lt;br /&gt;
&lt;br /&gt;
===Recent activity RSS feed preferences page===&lt;br /&gt;
&lt;br /&gt;
[[Image:activityrsspref.gif]]&lt;br /&gt;
&lt;br /&gt;
==Tasks and Timeline==&lt;br /&gt;
&lt;br /&gt;
* Further develop spec, get feedback, feel out implementation ✔&lt;br /&gt;
* Implement core functions - 1-2w ✔&lt;br /&gt;
* Secure existing RSS feeds in Moodle 1w ✔&lt;br /&gt;
*# Forums ✔&lt;br /&gt;
*# Blogs ✔&lt;br /&gt;
*# Database module ✔&lt;br /&gt;
*# Glossary ✔&lt;br /&gt;
* Add option to force HTTPS for RSS feeds ✔&lt;br /&gt;
* Add RSS to other areas of Moodle. &lt;br /&gt;
*# Calendar(Upcoming events) 1-2w ✔&lt;br /&gt;
*# Recent Activity 1-2w ✔&lt;br /&gt;
*# Assigments submitted 1w ✔&lt;br /&gt;
*# Messaging 1w ✔&lt;br /&gt;
* Upgrade whole RSS subsystem. 1-3w&lt;br /&gt;
*# Each module should have own function, that checks if there are any changes. ✔&lt;br /&gt;
*# Use ETag and If-Modified-Since headers. ✔&lt;br /&gt;
*# Generate RSS content on the fly(no cache files, no rss cron jobs) ✔&lt;br /&gt;
*# ContextId ✔&lt;br /&gt;
*# file.php (stub code) ✔&lt;br /&gt;
* Optional tasks - 1.5w&lt;br /&gt;
*# Give user an ability to reset his private keys ✔&lt;br /&gt;
*# Recent activity feed for &amp;quot;My courses&amp;quot; ✔&lt;br /&gt;
* Extensive debugging - 1w&lt;br /&gt;
* End-term evaluation&lt;br /&gt;
&lt;br /&gt;
== Glossary ==&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;Term&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Definition&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Hash value (also called a &amp;quot;digest&amp;quot; or a &amp;quot;checksum&amp;quot;) &lt;br /&gt;
| A concise representation of the longer message or document from which it was computed. The message digest is a sort of &amp;quot;digital fingerprint&amp;quot; of the larger document.&lt;br /&gt;
|-&lt;br /&gt;
| RSS feed &lt;br /&gt;
| A family of Web feed formats used to publish all kind of frequently updated content, usually blog entries, news headlines, and podcasts. RSS proved to be very convenient and easy-to-use, fast–to-implement technology, which makes users more productive and saves a lot of time.&lt;br /&gt;
|-&lt;br /&gt;
| user_private_key  &lt;br /&gt;
| unique hash-like string used for user identification. Stored in database.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
*[[GSOC/2008]]&lt;br /&gt;
* [[Student projects]]&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=96026 Project discussion thread]&lt;br /&gt;
*[http://tracker.moodle.org/browse/MDL-15122 Tracker issue related to project]&lt;br /&gt;
&lt;br /&gt;
[[Category:Project]]&lt;br /&gt;
[[Category:Developer|Feeds]]&lt;br /&gt;
[[Category:Feeds]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Student_projects/Secure_RSS_feeds&amp;diff=45112</id>
		<title>Student projects/Secure RSS feeds</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Student_projects/Secure_RSS_feeds&amp;diff=45112"/>
		<updated>2008-10-10T14:23:20Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Flagging this article as Obsolete design&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{obsolete_design}}&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
&amp;lt;p class=&amp;quot;note&amp;quot;&amp;gt;&#039;&#039;&#039;Note&#039;&#039;&#039;: This page outlines ideas for the &amp;quot;Secure RSS feeds&amp;quot; project. If you have any comments or suggestions, please add them to the [[Talk:Student projects/Secure RSS Feeds|page comments]].&#039;&#039;&amp;lt;/p&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Status == &lt;br /&gt;
&lt;br /&gt;
Release candidate. All main objectives complete. &lt;br /&gt;
&lt;br /&gt;
==Summary==&lt;br /&gt;
Secure RSS feeds is a project about making the RSS feeds published by Moodle secure so that only desired people can access the feeds. More details here.[http://code.google.com/soc/2008/moodle/appinfo.html?csaid=3141B6C0C1823EA1]&lt;br /&gt;
&lt;br /&gt;
Typical RSS URL will look like: “&amp;lt;nowiki&amp;gt;http://domain/moodle/rss/file.php/contextid/&amp;lt;/nowiki&amp;gt;&amp;lt;u&amp;gt;[[#hashkey|hash_key]]&amp;lt;/u&amp;gt;user_id/modulename/instance/any/other/params/module/wants/rss.xml”. &lt;br /&gt;
&lt;br /&gt;
Where [[#hashkey|hash_key]] – special hash-string used to identify user.&lt;br /&gt;
&lt;br /&gt;
User is identified by comparing part [[#hashkey|hash_key]] with the real hash value of user_id + user_private_key(from DB) +  modulename + instance(from URL) concatenation. &lt;br /&gt;
&lt;br /&gt;
If someone stole one private feed URL, he won’t be able to use it for reading other private feeds.&lt;br /&gt;
&lt;br /&gt;
==Security==&lt;br /&gt;
&lt;br /&gt;
# &amp;lt;div id=&amp;quot;hashkey&amp;quot;&amp;gt; Hash-key is a hash value from user_id, user_private_key, modulename (and other information, which is used to identify RSS feed) concatenation.  &amp;lt;/div&amp;gt;&lt;br /&gt;
# If hash-key is not specified, consider user as guest. &lt;br /&gt;
# In current version of spec, hashes are not additionally salted. &lt;br /&gt;
# User private keys are tied to context id&#039;s.&lt;br /&gt;
# There is an option to force https:// for all RSS feeds&lt;br /&gt;
&lt;br /&gt;
==Core functions==&lt;br /&gt;
&lt;br /&gt;
===rss_auth()===&lt;br /&gt;
&#039;&#039;&#039;rss_auth($hash_key, $user_id, $course_id, $context_id, $module, $instance, $info )&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;$hash_key&#039;&#039; - long hash-like string from URL.&lt;br /&gt;
* &#039;&#039;$user_id&#039;&#039; - user id from URL&lt;br /&gt;
* &#039;&#039;$course_id&#039;&#039; - the id of the course this feeds belongs to&lt;br /&gt;
* &#039;&#039;$context_id&#039;&#039; - the id of the context this feeds belongs to&lt;br /&gt;
* &#039;&#039;$module&#039;&#039; -  module name or course module object this feeds belongs to&lt;br /&gt;
* &#039;&#039;$instance&#039;&#039; - instance id. Could be blogid, forumid etc&lt;br /&gt;
* &#039;&#039;$info&#039;&#039; - additonal information, which is used to accurately identify RSS feed. Can be array.&lt;br /&gt;
&lt;br /&gt;
Authenticates user by hash-string in URL, sets up $USER and other necessary stuff(done by calling Moodle core function require_user_key_login()). Checks if the user can access particular course and module.&lt;br /&gt;
Function terminates with error if user doesn&#039;t have access to course\module.&lt;br /&gt;
&lt;br /&gt;
===rss_get_url_key()===&lt;br /&gt;
&#039;&#039;&#039;rss_get_url_key( $userid, $contextid, $modulename, $instance, $info)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
* &#039;&#039;$user&#039;&#039; - user id. &lt;br /&gt;
* &#039;&#039;$contextid&#039;&#039; - the id of the context this feeds belongs to&lt;br /&gt;
* &#039;&#039;$modulename&#039;&#039; -  module name this feeds belongs to&lt;br /&gt;
* &#039;&#039;$instance&#039;&#039; - instance id. Could be blogid, forumid etc&lt;br /&gt;
* &#039;&#039;$info&#039;&#039; - additonal information, which is used to accurately identify RSS feed. Can be array.&lt;br /&gt;
&lt;br /&gt;
Function returns long hash-like string, which can be used later to access specific RSS feed. Used when printing links.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===RSS feed generation===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;modulename_rss_newstuff($instance, $time,&amp;amp;$cache, $info)&#039;&#039;&#039;&lt;br /&gt;
This function checks if there is something new in module since $time&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;modulename_rss_generate_feed($instance, $context, $info, $cache)&#039;&#039;&#039;&lt;br /&gt;
This function generates and returns XML rss contents&lt;br /&gt;
&lt;br /&gt;
==Changes in RSS feed subsystem==&lt;br /&gt;
* No more Cron jobs for RSS feeds. &lt;br /&gt;
* All feeds are generated on the fly (i.e. no cached .xml files)&lt;br /&gt;
&lt;br /&gt;
Most of the times nothing changes in the feed - we do not have to send the actual feed content, we can just send HTTP 304 Not Modified header. And because no actual content is sent, this allows us to skip loading all capabilities, identifying users etc - improve performance. &lt;br /&gt;
&lt;br /&gt;
It may be convenient to prefetch some data in rss_newstuff(), that&#039;s why $cache is used. &lt;br /&gt;
However, it duplicates rcache functionality a bit, so I&#039;m thinking about removing it. &lt;br /&gt;
&lt;br /&gt;
But there is a problem, when rss_new_stuff() result depends on what capabilities user has. &lt;br /&gt;
In this situation during newstuff() check we assume that user has all the necessary capabilites. If there are no changes since last feed fetching - send 304 Not Modified. Otherwise, do the real check during feed content generation.&lt;br /&gt;
&lt;br /&gt;
==Database tables==&lt;br /&gt;
Fields added to existing tables.&lt;br /&gt;
&lt;br /&gt;
===course===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|rsstype&lt;br /&gt;
|int(1)&lt;br /&gt;
|0&lt;br /&gt;
|0 - disabled. 1 - recent activity rss&lt;br /&gt;
|-&lt;br /&gt;
|rssarticles&lt;br /&gt;
|int(2)&lt;br /&gt;
|0&lt;br /&gt;
|number of recent articles in RSS feed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===assigment===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|rsstype&lt;br /&gt;
|int(1)&lt;br /&gt;
|0&lt;br /&gt;
|0 - disabled. 1 - assignment submissions rss&lt;br /&gt;
|-&lt;br /&gt;
|rssarticles&lt;br /&gt;
|int(2)&lt;br /&gt;
|0&lt;br /&gt;
|number of recent articles in RSS feed&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
==Interface mockups==&lt;br /&gt;
&lt;br /&gt;
===RSS links on Course page===&lt;br /&gt;
[[Image:blocks.gif]]&lt;br /&gt;
&lt;br /&gt;
===Calendar RSS links===&lt;br /&gt;
[[Image:calendar_rss.gif]]&lt;br /&gt;
&lt;br /&gt;
[[Image:calblockrss.gif]]&lt;br /&gt;
&lt;br /&gt;
===Recent activity RSS feed preferences page===&lt;br /&gt;
&lt;br /&gt;
[[Image:activityrsspref.gif]]&lt;br /&gt;
&lt;br /&gt;
==Tasks and Timeline==&lt;br /&gt;
&lt;br /&gt;
* Further develop spec, get feedback, feel out implementation ✔&lt;br /&gt;
* Implement core functions - 1-2w ✔&lt;br /&gt;
* Secure existing RSS feeds in Moodle 1w ✔&lt;br /&gt;
*# Forums ✔&lt;br /&gt;
*# Blogs ✔&lt;br /&gt;
*# Database module ✔&lt;br /&gt;
*# Glossary ✔&lt;br /&gt;
* Add option to force HTTPS for RSS feeds ✔&lt;br /&gt;
* Add RSS to other areas of Moodle. &lt;br /&gt;
*# Calendar(Upcoming events) 1-2w ✔&lt;br /&gt;
*# Recent Activity 1-2w ✔&lt;br /&gt;
*# Assigments submitted 1w ✔&lt;br /&gt;
*# Messaging 1w ✔&lt;br /&gt;
* Upgrade whole RSS subsystem. 1-3w&lt;br /&gt;
*# Each module should have own function, that checks if there are any changes. ✔&lt;br /&gt;
*# Use ETag and If-Modified-Since headers. ✔&lt;br /&gt;
*# Generate RSS content on the fly(no cache files, no rss cron jobs) ✔&lt;br /&gt;
*# ContextId ✔&lt;br /&gt;
*# file.php (stub code) ✔&lt;br /&gt;
* Optional tasks - 1.5w&lt;br /&gt;
*# Give user an ability to reset his private keys ✔&lt;br /&gt;
*# Recent activity feed for &amp;quot;My courses&amp;quot; ✔&lt;br /&gt;
* Extensive debugging - 1w&lt;br /&gt;
* End-term evaluation&lt;br /&gt;
&lt;br /&gt;
== Glossary ==&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;Term&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Definition&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
| Hash value (also called a &amp;quot;digest&amp;quot; or a &amp;quot;checksum&amp;quot;) &lt;br /&gt;
| A concise representation of the longer message or document from which it was computed. The message digest is a sort of &amp;quot;digital fingerprint&amp;quot; of the larger document.&lt;br /&gt;
|-&lt;br /&gt;
| RSS feed &lt;br /&gt;
| A family of Web feed formats used to publish all kind of frequently updated content, usually blog entries, news headlines, and podcasts. RSS proved to be very convenient and easy-to-use, fast–to-implement technology, which makes users more productive and saves a lot of time.&lt;br /&gt;
|-&lt;br /&gt;
| user_private_key  &lt;br /&gt;
| unique hash-like string used for user identification. Stored in database.&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
*[[GSOC/2008]]&lt;br /&gt;
* [[Student projects]]&lt;br /&gt;
*[http://moodle.org/mod/forum/discuss.php?d=96026 Project discussion thread]&lt;br /&gt;
*[http://tracker.moodle.org/browse/MDL-15122 Tracker issue related to project]&lt;br /&gt;
&lt;br /&gt;
[[Category:Project]]&lt;br /&gt;
[[Category:Developer|Feeds]]&lt;br /&gt;
[[Category:Feeds]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Events&amp;diff=45111</id>
		<title>Development:Events</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Events&amp;diff=45111"/>
		<updated>2008-10-10T14:22:17Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Flagging this article as Obsolete design&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 [[Development: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;
* course_created&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:Developer|Events]]&lt;br /&gt;
[[Category:Grades]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Template:obsolete_design&amp;diff=45110</id>
		<title>Template:obsolete design</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Template:obsolete_design&amp;diff=45110"/>
		<updated>2008-10-10T14:18:13Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;notice metadata&amp;quot; id=&amp;quot;stub&amp;quot; style=&amp;quot;clear:both;&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;note&amp;quot;&amp;gt;&#039;&#039;This article is a design document which is &amp;lt;strong&amp;gt;no longer in use&amp;lt;/strong&amp;gt;, the code it describes having been written and added to the Moodle codebase, or simply considered then abandoned. The information it contains is likely to be &amp;lt;strong&amp;gt;out of date&amp;lt;/strong&amp;gt;, especially API and Database Schema specifications.&#039;&#039;&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;[[Category:Obsolete_Design]]&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;This template will categorize articles that include it into [[:Category:Obsolete_Design]].&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Obsolete_-_Moodle_forms_library&amp;diff=45109</id>
		<title>Development:Obsolete - Moodle forms library</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Obsolete_-_Moodle_forms_library&amp;diff=45109"/>
		<updated>2008-10-10T14:17:34Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==OBSOLETE INFORMATION==&lt;br /&gt;
{{obsolete_design}}&lt;br /&gt;
&#039;&#039;&#039;This was a proposal for discussion that was not used - you can see info about the development of the formslib [[Development:lib/formslib.php|here]]&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The OU has developed quite a nice in-house library to simplify creating Moodle editing forms. We would like to contribute it back to the community, and then as part of the accessibility work we are comissioning, get all Moodle forms modified to use it. However, before we can contribute it back, we need to get consensus withing the community that it is right for Moodle, and we need to tidy up the code a bit.&lt;br /&gt;
&lt;br /&gt;
This document outlines how the library should work after cleaning up, so the community can see whether they like it, and OU developers know what is involved in the cleanup.&lt;br /&gt;
&lt;br /&gt;
==What the system will be capable of==&lt;br /&gt;
&lt;br /&gt;
The library allows developers to create editing forms by creating a high-level representation of the form as a data-structure in memory, configuring all necessary options, and setting initial values of fields, and then the library generates the actual HTML of the form to include in the page.&lt;br /&gt;
&lt;br /&gt;
We are not planning to change significantly the way that Moodle editing forms look and function. However, by putting all the HTML (and JavaScript, CSS, ...) generation in one place, we make it much easier to make systematic improvements to accessibility, usability, client-side validation, etc. in future. Indeed, the OU&#039;s code already generates HTML that is a bit more accessible that most Moodle forms.&lt;br /&gt;
&lt;br /&gt;
By allowing developers to think at a higher level, we make their life easier, in the same way that datalib saves them from worrying about the details of SQL most of the time.&lt;br /&gt;
&lt;br /&gt;
The general way the library will work is that it will assume sensible defaults for everything to reduce the amount that has to be typed. So for a lot of input fields, you will only have to specify&lt;br /&gt;
&lt;br /&gt;
# the field type (e.g. text, think &amp;lt;input type=&amp;quot;&amp;quot; ... /&amp;gt;, However the hope would be to move towards higher-level types, such as integer, path, url, as in require_param, etc.)&lt;br /&gt;
# the field name (think &amp;lt;input name=&amp;quot;&amp;quot; ... /&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Everything else that is needed will be derived from these using sensible defaults. We need label text? We&#039;ll look up the field name in the lang file (each form will specify which lang file to use). We need a help link? Well, use /lang/en_utf8/help$langfile/$name.html. We need a field size? Each type will have a sensible default.&lt;br /&gt;
&lt;br /&gt;
However, if you want to override any of these defaults, you can by setting that option explicitly. Because there will be lots of options, and normally you will only need to change a few, the library will use a named attribute style, so example code would look like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$field = new text_field(&#039;fieldname&#039;);&lt;br /&gt;
$field-&amp;gt;set_set(&#039;size&#039;, 30);&lt;br /&gt;
$field-&amp;gt;set_set(&#039;label&#039;, &#039;lablestring&#039;);&lt;br /&gt;
&lt;br /&gt;
// or &lt;br /&gt;
&lt;br /&gt;
$field = new text_field(&#039;fieldname&#039;);&lt;br /&gt;
$field-&amp;gt;set_set(array(&#039;size&#039; =&amp;gt; 30, &#039;label&#039; =&amp;gt; &#039;lablestring&#039;));&lt;br /&gt;
&lt;br /&gt;
// or &lt;br /&gt;
&lt;br /&gt;
$field = new text_field(&#039;fieldname&#039;, array(&lt;br /&gt;
        &#039;size&#039; =&amp;gt; 30, &lt;br /&gt;
        &#039;label&#039; =&amp;gt; &#039;lablestring&#039;&lt;br /&gt;
));&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In this example, &#039;lablestring&#039; would again get looked up in the lang file automatically.&lt;br /&gt;
&lt;br /&gt;
For this situation, where there are lots of options available, but most people only need to change a few, I think this style of API works better than PHP function calls with lots of optional parameters. The options available for each field type will be documented in the comment where that class is defined.&lt;br /&gt;
&lt;br /&gt;
The library is designed to make it easy to add new field types, or new options to existing field types. Indeed, that is how the library has evolved so far: a bit at a time as needed. &lt;br /&gt;
&lt;br /&gt;
New field types are just new PHP classes, and they are likely to be subclasses of a base class that only change a few things. They don&#039;t even have to be defined in the central form library. If you need an specialist field type only within one (e.g. contrib) module, you can create a new field-type subclass in that module code and use it in that modules forms without having to touch core code. And if we later want to move that field type into core, it is trivial to move the class definition.&lt;br /&gt;
&lt;br /&gt;
Since we will have to tidy up the code anyway, all the function/class/method/option names in the API are potentially changable.&lt;br /&gt;
&lt;br /&gt;
==PHP API==&lt;br /&gt;
&lt;br /&gt;
Each thing in the API will be a PHP class, these basically fall into three categories: the whole form, groupings of form fields, and individual field types. Note that the groupings are currently only used for functional reasons, like showing or hiding groups of elements. Logical groupings, that would correspond to &amp;lt;fieldset&amp;gt; tags for accessibility, are not included yet, but could be added.&lt;br /&gt;
&lt;br /&gt;
I am just going to list all the possible method calls. I will assume that it is clear what they do from their names (and I am running out of time).&lt;br /&gt;
&lt;br /&gt;
===The whole form===&lt;br /&gt;
&lt;br /&gt;
There is a class to represent a form, and most of the time you will be able to do everything you want to do by calling methods on this class, and you don&#039;t have to worry about the rest of the API.&lt;br /&gt;
&lt;br /&gt;
 $mf = new moodle_form($langfile);&lt;br /&gt;
&lt;br /&gt;
 $field = &amp;amp;$mf-&amp;gt;add($fieldtype, $fieldname, $fieldoptions, $insertbefore);&lt;br /&gt;
 $field = &amp;amp;$mf-&amp;gt;add_item($fieldobject, $insertbefore); // $insertbefore is optional. By default things are inserted at the end of the form.&lt;br /&gt;
 $field = &amp;amp;$mf-&amp;gt;remove($fieldname);&lt;br /&gt;
 $field = &amp;amp;$mf-&amp;gt;get($fieldname);&lt;br /&gt;
&lt;br /&gt;
These let you add or remove things from the form. The field you have just done stuff to is returned, but most of the time you will not bother to do anything with the return value. These method work with groups, etc. not just fields.&lt;br /&gt;
&lt;br /&gt;
 $mf-&amp;gt;set_action($url);&lt;br /&gt;
 $mf-&amp;gt;set_init_html_editor(); // There was a reason why the library can&#039;t guess this from the list of fields.&lt;br /&gt;
 $mf-&amp;gt;set_submit_caption($langstringid);&lt;br /&gt;
 $mf-&amp;gt;set_hidden($fieldname, $value);&lt;br /&gt;
 $mf-&amp;gt;set_value($fieldname, $value); // The initial value for this field. (or use the $form attribute on show()).  &lt;br /&gt;
 $mf-&amp;gt;set_error($fieldname, $message); // Error message to be displayed in red next to this field if you are doing server-side validation.&lt;br /&gt;
&lt;br /&gt;
 $mf-&amp;gt;show($action, $form);&lt;br /&gt;
&lt;br /&gt;
Show the form. $form is an object holding the default value of each field that has not already been set using set_value().&lt;br /&gt;
&lt;br /&gt;
 $mf-&amp;gt;add_xhtml($htmlphpcode, $insertbefore);&lt;br /&gt;
 $mf-&amp;gt;set_xhtml_param($name, $value);&lt;br /&gt;
&lt;br /&gt;
You can insert arbitrary bits of HTML or PHP code into the form using this. When outputting the form, the library does &#039;&#039;&#039;eval(&#039;?&amp;gt;&#039;.$htmlphpcode.&#039;&amp;lt;?php &#039;);&#039;&#039;&#039; in a place where $form is in scope, and the variables passed in via set_xhtml_param are available from an array as $xmlform[$name].&lt;br /&gt;
&lt;br /&gt;
 $new_mf = moodle_form::loadfile($xmlfilename);&lt;br /&gt;
 $new_mf = moodle_form::loadstring($xmlstring);&lt;br /&gt;
&lt;br /&gt;
Create a whole form using the XML syntax mentioned below.&lt;br /&gt;
&lt;br /&gt;
===Fields===&lt;br /&gt;
&lt;br /&gt;
The library also has classes representing the different field types, which you can use if you want.&lt;br /&gt;
&lt;br /&gt;
 $field = new xxxx_field($name, $options_array); // where xxxx is one of the field types below.&lt;br /&gt;
 $field-&amp;gt;set($optionname, $optionvalue);&lt;br /&gt;
 $field-&amp;gt;set($optionarray); // Array of $optionname =&amp;gt; $optionvalue pairs.&lt;br /&gt;
 $optionvalue = $field-&amp;gt;get($optionname);&lt;br /&gt;
&lt;br /&gt;
 $field-&amp;gt;set_value($value); // Sets the initial value of this field.&lt;br /&gt;
&lt;br /&gt;
 $field-&amp;gt;output($form); // Print this form element. &lt;br /&gt;
&lt;br /&gt;
Normally you won&#039;t call output() directly. You will call $mf-&amp;gt;output, which will call $field-&amp;gt;output on each field for you, but you can use this yourself if you just want to print one form control anywhere you like, not as part of a form.&lt;br /&gt;
&lt;br /&gt;
===Groups===&lt;br /&gt;
&lt;br /&gt;
Groups (which will probably get renamed to conditionalshow, but I don&#039;t want to change all the examples just now) allow you to show or hide a collection of fields, depending on the setting of another field.&lt;br /&gt;
&lt;br /&gt;
groups have basically the same methods as fields, but $name is optional. $name has no function, unless you need to identify the group later for a call the $mf-&amp;gt;get($groupname);&lt;br /&gt;
&lt;br /&gt;
There are a few extra methods they have over fields:&lt;br /&gt;
&lt;br /&gt;
 $field = &amp;amp;$group-&amp;gt;add($fieldtype, $fieldname, $fieldoptions, $insertbefore);&lt;br /&gt;
 $field = &amp;amp;$group-&amp;gt;add_item($fieldobject, $insertbefore);&lt;br /&gt;
 $field = &amp;amp;$group-&amp;gt;remove($fieldname);&lt;br /&gt;
 $field = &amp;amp;$group-&amp;gt;get($fieldname);&lt;br /&gt;
&lt;br /&gt;
 $group-&amp;gt;set_condition($conditionfield, $conditionvalue);&lt;br /&gt;
&lt;br /&gt;
The elements within the group will only be visible when the field $conditionfield elsewhere in the form has value $conditionvalue. This works best when $conditionfield is a dropdown.&lt;br /&gt;
&lt;br /&gt;
Do we also want a conditionalenable group that enables/disables a bunch of fields, rather than showing or hiding them?&lt;br /&gt;
&lt;br /&gt;
===Multiples===&lt;br /&gt;
&lt;br /&gt;
This lets you do things like enter multiple usernames, as in the &#039;working example&#039; below. You would also use it on the editing form for multiple choice questions, to enter any number of answers with matching grades.&lt;br /&gt;
&lt;br /&gt;
Again, they support the same methods as fields, and also:&lt;br /&gt;
&lt;br /&gt;
 $field = &amp;amp;$multiple-&amp;gt;add($fieldtype, $fieldname, $fieldoptions, $insertbefore);&lt;br /&gt;
 $field = &amp;amp;$multiple-&amp;gt;add_item($fieldobject, $insertbefore);&lt;br /&gt;
 $field = &amp;amp;$multiple-&amp;gt;remove($fieldname);&lt;br /&gt;
 $field = &amp;amp;$multiple-&amp;gt;get($fieldname);&lt;br /&gt;
&lt;br /&gt;
 $multiple-&amp;gt;set_max($number);&lt;br /&gt;
&lt;br /&gt;
For accessibility reasons, multiples work by printing a certain number of copies into the HTML, and these are then shown or hidden by the JavaScript. This is for accessibility reasons, and so the forms work without JavaScript. max is the number of copies that are printed. It defaults to 10.&lt;br /&gt;
&lt;br /&gt;
==Field types initially supported==&lt;br /&gt;
&lt;br /&gt;
All field will support the options:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;lable&#039;&#039;&#039; the field lable. This is a string that is looked up in the language file. Or, if the string starts with an &#039;=&#039; character, then this is trimmed off, and the rest of the string is used literally. Defaults to name.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;help&#039;&#039;&#039; the name of the help file to link to. Defaults to name. Setting to &#039;&#039; removes the help icon.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;helpalt&#039;&#039;&#039; the langstring to use for the tooltip of the help icon.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;optional&#039;&#039;&#039; if set to &#039;yes&#039;, then adds a checkbox before the field to enable or diable it. The checkbox&#039;s name is name is constructed by taking the field name and appending &#039;enable&#039;. If you want another name, set this option to the checkbox name, instead of &#039;yes&#039;.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;needcapability&#039;&#039;&#039; this is mainly aimed at 1.7 roles and permissions. Will hide this field unless the user has the named capability. At first, this will only recognise the values admin, teacher, student, noneditingteacher, guest, and translate these into the obvious isadmin() type calls.&lt;br /&gt;
&lt;br /&gt;
More options will probably get added in future.&lt;br /&gt;
&lt;br /&gt;
===display===&lt;br /&gt;
&lt;br /&gt;
Just diplay a value with a label, the value can&#039;t be edited.&lt;br /&gt;
&lt;br /&gt;
===text===&lt;br /&gt;
&lt;br /&gt;
Text input box.&lt;br /&gt;
&lt;br /&gt;
Supports the additional options:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;required&#039;&#039;&#039; a regexp that is used for client-side validation against that regexp.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;size&#039;&#039;&#039; as in &amp;lt;input size=&amp;quot;&amp;quot; ... /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===file===&lt;br /&gt;
&lt;br /&gt;
File upload box.&lt;br /&gt;
&lt;br /&gt;
===date===&lt;br /&gt;
&lt;br /&gt;
Date, like quiz open and close dates.&lt;br /&gt;
&lt;br /&gt;
===html===&lt;br /&gt;
&lt;br /&gt;
The standard HTML editor, or just a text area, depending on the settings. (This calls print_textarea).&lt;br /&gt;
&lt;br /&gt;
===dropdown===&lt;br /&gt;
&lt;br /&gt;
A dropdown menu. This field has the additional methods:&lt;br /&gt;
&lt;br /&gt;
 $dropdown-&amp;gt;add_option($value, $label);&lt;br /&gt;
 $dropdown-&amp;gt;remove_option($value);&lt;br /&gt;
&lt;br /&gt;
$label is optional. By default: if $value is an integer, use that integer as the label, otherwise look $value up in the langfile.&lt;br /&gt;
&lt;br /&gt;
Dropdown supports the additional option:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;default&#039;&#039;&#039; which option to select by default.&lt;br /&gt;
&lt;br /&gt;
===radio===&lt;br /&gt;
&lt;br /&gt;
A set of linked radio buttons, which are defined in the same way as dropdown menu options.&lt;br /&gt;
&lt;br /&gt;
===multiselect===&lt;br /&gt;
&lt;br /&gt;
A list box where you can select multiple options, which are defined in the same way as dropdown menu options.&lt;br /&gt;
&lt;br /&gt;
Actually, I think we should change the implementation of this to use a set of checkboxes when there are fewer than about a dozen options, and automatically switch to useing a list box when there are more than that. And maybe add an optional parameter to force the listbox/checkbox decision one way or the other.&lt;br /&gt;
&lt;br /&gt;
===yesno===&lt;br /&gt;
&lt;br /&gt;
A dropdown menu with just the two options &#039;yes&#039; and &#039;no&#039;.&lt;br /&gt;
&lt;br /&gt;
===user===&lt;br /&gt;
&lt;br /&gt;
A flashy text box where you can enter user&#039;s names, and it does AJAX stuff to help you auto-complete them.&lt;br /&gt;
&lt;br /&gt;
===visible===&lt;br /&gt;
&lt;br /&gt;
This generates the standard &amp;quot;Visible to students&amp;quot; field that appears on add/update module forms.&lt;br /&gt;
&lt;br /&gt;
===groupmode===&lt;br /&gt;
&lt;br /&gt;
This generates the standard &amp;quot;Group mode&amp;quot; field that appears on add/update module forms.&lt;br /&gt;
&lt;br /&gt;
==XML form definition format==&lt;br /&gt;
&lt;br /&gt;
This provides the quickest way to create most forms. It should be clear how this translates into the PHP API calls defined above. XML elements $mf-&amp;gt;add() calls. XML attributes correspond either to required information, or to set_opt calls. Form filds, like dropdowns, that need extra information get it from child elements. See the examples below for what it looks like.&lt;br /&gt;
&lt;br /&gt;
==A working example==&lt;br /&gt;
&lt;br /&gt;
Here is an example of a form produced with the current (un-cleaned-up) version of the OU&#039;s library. The form looks like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:Formproposal_Add_newsfeed_form.png]]&lt;br /&gt;
&lt;br /&gt;
There is some OU-specific stuff here, like presentation and authids, and the fact that we reveal $user-&amp;gt;username to teachers. I am assuming you can filter that out.&lt;br /&gt;
&lt;br /&gt;
More interestingly, notice that&lt;br /&gt;
&lt;br /&gt;
* The required field Name has not been filled it, so its label is red, and the create button is disabled. Below, you will see that anything typed into this field will actually be validated against a regular expression.&lt;br /&gt;
* The user field type does cool AJAX to save you typing the whole name.&lt;br /&gt;
* After you have added one user as a poster, a second box appeared where we could type a second user name. And when we finish typing here, a third box will appear.&lt;br /&gt;
&lt;br /&gt;
The XML defining the form looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;editform langfile=&#039;block_newsfeed&#039;&amp;gt;&lt;br /&gt;
     &amp;lt;text name=&#039;location&#039;/&amp;gt;&lt;br /&gt;
     &amp;lt;text name=&#039;name&#039; required=&#039;\S&#039;/&amp;gt;&lt;br /&gt;
     &amp;lt;text name=&#039;pres&#039; required=&#039;^([0-9]{2}[a-zA-Z]?)?$&#039;/&amp;gt;&lt;br /&gt;
     &amp;lt;html name=&#039;summary&#039;/&amp;gt;&lt;br /&gt;
     &amp;lt;dropdown name=&#039;type&#039;&amp;gt;&lt;br /&gt;
       &amp;lt;option value=&#039;internal&#039;&amp;gt;type_internal&amp;lt;/option&amp;gt;&lt;br /&gt;
       &amp;lt;option value=&#039;external&#039;&amp;gt;type_external&amp;lt;/option&amp;gt;&lt;br /&gt;
     &amp;lt;/dropdown &amp;gt;&lt;br /&gt;
     &amp;lt;group requiredname=&amp;quot;type&amp;quot; requiredvalue=&amp;quot;external&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;item type=&#039;text&#039; name=&#039;url&#039;/&amp;gt;&lt;br /&gt;
     &amp;lt;/group&amp;gt;&lt;br /&gt;
     &amp;lt;group requiredname=&amp;quot;type&amp;quot; requiredvalue=&amp;quot;internal&amp;quot;&amp;gt;&lt;br /&gt;
         &amp;lt;date name=&#039;startdate&#039;/&amp;gt;&lt;br /&gt;
         &amp;lt;dropdown name=&#039;public&#039;&amp;gt;&lt;br /&gt;
           &amp;lt;option value=&#039;1&#039;&amp;gt;access_public&amp;lt;/option&amp;gt;&lt;br /&gt;
           &amp;lt;option value=&#039;0&#039;&amp;gt;access_private&amp;lt;/option&amp;gt;&lt;br /&gt;
         &amp;lt;/dropdown &amp;gt;&lt;br /&gt;
         &amp;lt;text name=&#039;defaultauthid&#039;/&amp;gt;&lt;br /&gt;
         &amp;lt;multiple&amp;gt;&lt;br /&gt;
             &amp;lt;text name=&#039;optionalauthids&#039; required=&#039;^([A-Z0-9]+)?$&#039;/&amp;gt;&lt;br /&gt;
         &amp;lt;/multiple&amp;gt;&lt;br /&gt;
         &amp;lt;multiple&amp;gt;&lt;br /&gt;
             &amp;lt;user name=&#039;posters&#039;/&amp;gt;&lt;br /&gt;
         &amp;lt;/multiple&amp;gt;&lt;br /&gt;
         &amp;lt;multiple&amp;gt;&lt;br /&gt;
             &amp;lt;user name=&#039;approvers&#039;/&amp;gt;&lt;br /&gt;
         &amp;lt;/multiple&amp;gt;&lt;br /&gt;
     &amp;lt;/group&amp;gt;          &lt;br /&gt;
&amp;lt;/editform&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The PHP code that creates the form definition ($xf), and the object with all the current values ($form) and sets all the default values looks like this:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$xf=xml_form::load_file(&#039;editfeed.xml&#039;);&lt;br /&gt;
$xf-&amp;gt;set_init_html_editor(true);&lt;br /&gt;
$form=new stdClass;&lt;br /&gt;
$newsfeedid=optional_param(&#039;newsfeedid&#039;,0,PARAM_INT);&lt;br /&gt;
if($newsfeedid) {&lt;br /&gt;
    $nf=feed_system::$inst-&amp;gt;get_feed($newsfeedid);&lt;br /&gt;
    $form-&amp;gt;location=$nf-&amp;gt;get_folder()-&amp;gt;get_path();&lt;br /&gt;
    $xf-&amp;gt;set_hidden(&#039;newsfeedid&#039;,$newsfeedid);&lt;br /&gt;
    &lt;br /&gt;
    $form-&amp;gt;name=$nf-&amp;gt;get_name();&lt;br /&gt;
    $form-&amp;gt;pres=$nf-&amp;gt;get_pres();&lt;br /&gt;
    if($form-&amp;gt;pres==null) {&lt;br /&gt;
        $form-&amp;gt;pres=&#039;&#039;;&lt;br /&gt;
    }&lt;br /&gt;
    $form-&amp;gt;summary=$nf-&amp;gt;get_summary();&lt;br /&gt;
    if(is_a($nf,&#039;external_news_feed&#039;)) {&lt;br /&gt;
        $form-&amp;gt;type=&#039;external&#039;;&lt;br /&gt;
        $form-&amp;gt;url=$form-&amp;gt;get_url();&lt;br /&gt;
    } else {&lt;br /&gt;
        $form-&amp;gt;type=&#039;internal&#039;;&lt;br /&gt;
        $form-&amp;gt;startdate=$nf-&amp;gt;get_start_date();&lt;br /&gt;
        $form-&amp;gt;public=$nf-&amp;gt;is_public();&lt;br /&gt;
        $form-&amp;gt;defaultauthid=$nf-&amp;gt;get_default_authid();&lt;br /&gt;
        xml_form::set_multiple($form,&#039;optionalauthids&#039;,$nf-&amp;gt;get_optional_authids());&lt;br /&gt;
        xml_form::set_multiple($form,&#039;posters&#039;,$nf-&amp;gt;get_poster_usernames());&lt;br /&gt;
        xml_form::set_multiple($form,&#039;approvers&#039;,$nf-&amp;gt;get_approver_usernames());&lt;br /&gt;
        $form-&amp;gt;newsfeedid=$newsfeedid;&lt;br /&gt;
    }&lt;br /&gt;
    &lt;br /&gt;
} else {&lt;br /&gt;
    $folderid=required_param(&#039;folderid&#039;,PARAM_INT);&lt;br /&gt;
    $lf=feed_system::$inst-&amp;gt;get_location_folder($folderid,null);&lt;br /&gt;
    $nf=null;&lt;br /&gt;
    $xf-&amp;gt;set_submit_caption(get_string(&#039;create&#039;));&lt;br /&gt;
    $form-&amp;gt;location=$lf-&amp;gt;get_path();&lt;br /&gt;
    $xf-&amp;gt;set_hidden(&#039;newsfeedid&#039;,0);&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// ... print_header and other boring bits.&lt;br /&gt;
&lt;br /&gt;
print_simple_box_start(&#039;center&#039;);&lt;br /&gt;
$xf-&amp;gt;show(basename(__FILE__), $form);&lt;br /&gt;
print_simple_box_end();&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Examples of converting existing Moodle forms==&lt;br /&gt;
&lt;br /&gt;
These are four example forms that Marting Dougiamas asked to see how we would handle.&lt;br /&gt;
&lt;br /&gt;
===Add resource -&amp;gt; Link to a file or web site===&lt;br /&gt;
&lt;br /&gt;
[[Image:Formproposal_Add link resource.png]]&lt;br /&gt;
&lt;br /&gt;
I have taken the liberty of changing the way the window options work on this form. Instead of having a show/hide window settings button, and when that is turned on showing all the options for both same window and new window, I have changed it to be a same window/popup window dropdown, and depending on the setting there, showing or hiding the particular set of options. It would also be possible to use the library to generate the existing form.&lt;br /&gt;
&lt;br /&gt;
This form contains some very specific bits, namely the location field and the parameters sections. For now I have kept the existing code to generate these bits. If fields like the location field were used in other places, we could add a url field type. I don&#039;t see any future in generalising the parameters bit, though the code to generate it could be cleaned up. I just copied and pasted the existing code, and made the minimal changes.&lt;br /&gt;
&lt;br /&gt;
I&#039;ve used a multiselect for the window options. That will require a small change to the response processing code. &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mf = moodle_form::loadstring(&#039;&lt;br /&gt;
&amp;lt;editform langfile=&amp;quot;resource&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;text name=&amp;quot;name&amp;quot; help=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;htmlarea name=&amp;quot;summary&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;xhtml label=&amp;quot;location&amp;quot;&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
        echo &amp;quot;&amp;lt;input type=\&amp;quot;text\&amp;quot; name=\&amp;quot;reference\&amp;quot; size=\&amp;quot;90\&amp;quot; value=\&amp;quot;$form-&amp;gt;reference\&amp;quot; alt=\&amp;quot;reference\&amp;quot; /&amp;gt;&amp;lt;br /&amp;gt;&amp;quot;;&lt;br /&gt;
        button_to_popup_window (&amp;quot;/files/index.php?id=$form-&amp;gt;course&amp;amp;amp;choose=form.reference&amp;quot;, &amp;quot;coursefiles&amp;quot;, $xmlform[&#039;strchooseafile&#039;], 500, 750, $xmlform[&#039;strchooseafile&#039;]);&lt;br /&gt;
        echo &amp;quot;&amp;lt;input type=\&amp;quot;button\&amp;quot; name=\&amp;quot;searchbutton\&amp;quot; value=\&amp;quot;$xmlform[&#039;strsearch&#039;] ...\&amp;quot; &amp;quot;.&lt;br /&gt;
             &amp;quot;onclick=\&amp;quot;return window.open(&#039;$CFG-&amp;gt;resource_websearch&#039;, &#039;websearch&#039;, &#039;menubar=1,location=1,directories=1,toolbar=1,scrollbars,resizable,width=800,height=600&#039;);\&amp;quot; /&amp;gt;\n&amp;quot;;&lt;br /&gt;
        if ($CFG-&amp;gt;resource_allowlocalfiles) {&lt;br /&gt;
            button_to_popup_window (&amp;quot;/mod/resource/type/file/localfile.php?choose=form.reference&amp;quot;, &lt;br /&gt;
            &amp;quot;localfiles&amp;quot;, get_string(&#039;localfilechoose&#039;, &#039;resource&#039;), 400, 600, &lt;br /&gt;
            get_string(&#039;localfilechoose&#039;, &#039;resource&#039;));&lt;br /&gt;
        }&lt;br /&gt;
    ]]&amp;gt;&amp;lt;/xhtml&amp;gt;&lt;br /&gt;
    &amp;lt;url name=&amp;quot;location&amp;quot; showuploadbutton=&amp;quot;1&amp;quot; showsearchbutton=&amp;quot;1&amp;quot; help=&amp;quot;&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;dropdown name=&amp;quot;windowpopup&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&#039;0&#039;&amp;gt;pagewindow&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&#039;1&#039;&amp;gt;newwindow&amp;lt;/option&amp;gt;&lt;br /&gt;
    &amp;lt;/dropdown&amp;gt;&lt;br /&gt;
    &amp;lt;group requiredname=&amp;quot;newwindow&amp;quot; requiredvalue=&amp;quot;0&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;hidden name=&amp;quot;hframepage&amp;quot; value=&amp;quot;0&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;multiselect name=&amp;quot;framepage&amp;quot; help=&amp;quot;&amp;quot;&amp;gt;&lt;br /&gt;
            &amp;lt;option value=&amp;quot;1&amp;quot;&amp;gt;frameifpossible&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;/multiselect&amp;gt;&lt;br /&gt;
    &amp;lt;/group&amp;gt;&lt;br /&gt;
    &amp;lt;group requiredname=&amp;quot;newwindow&amp;quot; requiredvalue=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;multiselect name=&amp;quot;windowoptions&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;/multiselect&amp;gt;&lt;br /&gt;
        &amp;lt;integer name=&amp;quot;resource_popupwidth&amp;quot; label=&amp;quot;resource_popupwidth&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;integer name=&amp;quot;resource_popupheight&amp;quot; label=&amp;quot;resource_popupheight&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/group&amp;gt;&lt;br /&gt;
    &amp;lt;yesno name=&amp;quot;parameters&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;group requiredname=&amp;quot;newwindow&amp;quot; requiredvalue=&amp;quot;1&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;xhtml&amp;gt;&amp;lt;![CDATA[&lt;br /&gt;
&amp;lt;table align=&amp;quot;center&amp;quot;&amp;gt;&lt;br /&gt;
&lt;br /&gt;
        &amp;lt;tr&amp;gt;&lt;br /&gt;
            &amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;?php print_string(&amp;quot;parameter&amp;quot;, &amp;quot;resource&amp;quot;) ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
            &amp;lt;td align=&amp;quot;center&amp;quot;&amp;gt;&amp;lt;?php print_string(&amp;quot;variablename&amp;quot;, &amp;quot;resource&amp;quot;) ?&amp;gt;&amp;lt;/td&amp;gt;&lt;br /&gt;
        &amp;lt;/tr&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;?php&lt;br /&gt;
&lt;br /&gt;
for ($i=0; $i &amp;lt; $xmlform[&#039;maxparameters&#039;]; $i++) {&lt;br /&gt;
    echo &amp;quot;&amp;lt;tr&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;td valign=\&amp;quot;top\&amp;quot;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;select name=\&amp;quot;parameter$i\&amp;quot;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;option value=\&amp;quot;-\&amp;quot;&amp;gt;-- &amp;quot;.get_string(&#039;chooseparameter&#039;, &#039;resource&#039;).&amp;quot; --&amp;lt;/option&amp;gt;\n&amp;quot;;&lt;br /&gt;
    foreach ($xmlform[&#039;parameters&#039;] as $field=&amp;gt;$fieldarr) {&lt;br /&gt;
        if ($fieldarr[&#039;value&#039;] === &amp;quot;optgroup&amp;quot;) {&lt;br /&gt;
            echo &amp;quot;&amp;lt;optgroup label=\&amp;quot;{$fieldarr[&#039;langstr&#039;]}\&amp;quot;&amp;gt;\n&amp;quot;;&lt;br /&gt;
        } elseif ($fieldarr[&#039;value&#039;] === &amp;quot;/optgroup&amp;quot;) {&lt;br /&gt;
            echo &amp;quot;&amp;lt;/optgroup&amp;gt;\n&amp;quot;;&lt;br /&gt;
        } else {&lt;br /&gt;
            echo &amp;quot;&amp;lt;option value=\&amp;quot;$field\&amp;quot;&amp;quot;;&lt;br /&gt;
            if ($xmlform[&#039;alltextfield&#039;][$i][&#039;parameter&#039;] == $field) {&lt;br /&gt;
                echo &amp;quot; selected=\&amp;quot;selected\&amp;quot;&amp;quot;;&lt;br /&gt;
            }&lt;br /&gt;
            echo &amp;quot;&amp;gt;{$fieldarr[&#039;langstr&#039;]}&amp;lt;/option&amp;gt;\n&amp;quot;;&lt;br /&gt;
        }&lt;br /&gt;
    }&lt;br /&gt;
    echo &amp;quot;&amp;lt;/select&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;/td&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;td valign=\&amp;quot;top\&amp;quot;&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;input type=\&amp;quot;text\&amp;quot; name=\&amp;quot;parse$i\&amp;quot; value=\&amp;quot;{$xmlform[&#039;alltextfield&#039;][$i][&#039;parse&#039;]}\&amp;quot; alt=\&amp;quot;parameter$i\&amp;quot;/&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;/td&amp;gt;\n&amp;quot;;&lt;br /&gt;
    echo &amp;quot;&amp;lt;/tr&amp;gt;\n&amp;quot;;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
?&amp;gt;&lt;br /&gt;
&amp;lt;/table&amp;gt;&lt;br /&gt;
    ]]&amp;gt;&amp;lt;/xhtml&amp;gt;&lt;br /&gt;
    &amp;lt;/group&amp;gt;&lt;br /&gt;
&#039;)&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param(&#039;strchooseafile&#039;, get_string(...));&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param(&#039;strsearch&#039;, get_string(...));&lt;br /&gt;
&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param(&#039;maxparameters&#039;, $this-&amp;gt;maxparameters);&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param(&#039;parameters&#039;, $this-&amp;gt;parameters);&lt;br /&gt;
$mf-&amp;gt;set_xhtml_param(&#039;alltextfield&#039;, $alltextfield); // Assuming that this code is after the end of setup() in mod\resource\type\file\resource.class.php&lt;br /&gt;
&lt;br /&gt;
$winopt = $mf-&amp;gt;get(&#039;windowoptions&#039;);&lt;br /&gt;
foreach ($RESOURCE_WINDOW_OPTIONS as $optionname) {&lt;br /&gt;
    $defaultvalue = &amp;quot;resource_popup$optionname&amp;quot;;&lt;br /&gt;
    $form-&amp;gt;$optionname = $CFG-&amp;gt;$defaultvalue;&lt;br /&gt;
    if ($optionname != &#039;height&#039; &amp;amp;&amp;amp; $optionname != &#039;width&#039;) {&lt;br /&gt;
        $winopt-&amp;gt;add_option($optionname, &amp;quot;str$optionname&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Add quiz===&lt;br /&gt;
&lt;br /&gt;
[[Image:Formproposal_Add quiz form.png]]&lt;br /&gt;
&lt;br /&gt;
To convert this form, we need to do something special for the &amp;quot;students may review&amp;quot; bit. You could either invent an new &amp;lt;optiongrid&amp;gt; type, which would be quite easy to implement. That is the option used below. Alternatively, you could use the feature for including arbitrary HTML and PHP in the form, and keep the existing code for generating this part of the form. Since the existing layout breaks when you make the browser window narrow, as in the screenshot, I would be inclined to redo it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mf = moodle_form::loadstring(&#039;&lt;br /&gt;
&amp;lt;editform langfile=&amp;quot;quiz&amp;quot; help=&amp;quot;&amp;quot;&amp;gt;&lt;br /&gt;
    &amp;lt;text name=&amp;quot;name&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;htmlarea name=&amp;quot;introduction&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;date name=&amp;quot;available&amp;quot; label=&amp;quot;quizopen&amp;quot; optional=&amp;quot;yes&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;date name=&amp;quot;due&amp;quot; label=&amp;quot;quizclose&amp;quot; optional=&amp;quot;yes&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;real name=&amp;quot;timelimit&amp;quot; lableafter=&amp;quot;minutes&amp;quot; helpalt=&amp;quot;quiztimer&amp;quot; optional=&amp;quot;yes&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;dropdown name=&amp;quot;questionsperpage&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;option value=&#039;0&#039;&amp;gt;unlimited&amp;lt;/option&amp;gt;&lt;br /&gt;
        &amp;lt;!-- other options will be added programmatically --&amp;gt;&lt;br /&gt;
    &amp;lt;/dropdown&amp;gt;&lt;br /&gt;
    &amp;lt;yesno name=&amp;quot;shufflequestions&amp;quot;/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;!-- ... skipping the next few boring fields ... --&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;optiongrid name=&amp;quot;reviewoptions&amp;quot;&amp;gt;&lt;br /&gt;
        &amp;lt;col name=&amp;quot;responses&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;col name=&amp;quot;scores&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;col name=&amp;quot;feedback&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;col name=&amp;quot;answers&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;row name=&amp;quot;immediately&amp;quot; lable=&amp;quot;reviewimmediately&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;row name=&amp;quot;open&amp;quot; lable=&amp;quot;reviewopen&amp;quot;/&amp;gt;&lt;br /&gt;
        &amp;lt;row name=&amp;quot;closed&amp;quot; lable=&amp;quot;reviewclosed&amp;quot;/&amp;gt;&lt;br /&gt;
    &amp;lt;/optiongrid&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    &amp;lt;!-- ... skipping the next few boring fields ... --&amp;gt;&lt;br /&gt;
    &lt;br /&gt;
    &amp;lt;groupmode/&amp;gt;&lt;br /&gt;
    &amp;lt;visible/&amp;gt;&lt;br /&gt;
&#039;)&lt;br /&gt;
$questionsperpage =&amp;amp; $mf-&amp;gt;get(&#039;questionsperpage&#039;);&lt;br /&gt;
for ($i = 1; $i &amp;lt;= 50; $i += 1) {&lt;br /&gt;
    $questionsperpage-&amp;gt;add_option($i);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This does not take into account the settings on http://&#039;&#039;example.com&#039;&#039;/moodle/admin/module.php?module=quiz, which lets the admin move certain quiz options to be hidden behind an &amp;quot;advanced&amp;quot; option. To implement this you would need to add a show/hide advanced control (I would do this as a &amp;lt;yesno name=&amp;quot;showadvanced&amp;quot;/&amp;gt;, and an empty advanced group, then use some PHP code like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$fix = 0;&lt;br /&gt;
$advgroup = $mf-&amp;gt;get_field(&#039;advanced&#039;);&lt;br /&gt;
&lt;br /&gt;
if ($CFG-&amp;gt;quiz_fix_timelimit) {&lt;br /&gt;
    $item = &amp;amp;$mf-&amp;gt;remove(&#039;timelimit&#039;);&lt;br /&gt;
    $advgroup-&amp;gt;add($item)&lt;br /&gt;
    $fix = 1;&lt;br /&gt;
}&lt;br /&gt;
// ... and so on, for all the other options. Or you could try to be clever &lt;br /&gt;
// and do this as a loop over an array of field names ... then&lt;br /&gt;
if (!$fix) {&lt;br /&gt;
    $form-&amp;gt;remove(&#039;showadvanced&#039;);&lt;br /&gt;
    $form-&amp;gt;remove(&#039;advanced&#039;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Add database===&lt;br /&gt;
&lt;br /&gt;
[[Image:Formproposal_Add_database_form.png]]&lt;br /&gt;
&lt;br /&gt;
I won&#039;t type out full code for this example most of it is simple, and it should be clear how to do it given the above examples, just comment on a couple of things:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Entries required before viewing&#039;&#039;&#039; I assume this is only indented because the label is so long that having it all lined up would break the table layout. I think our code using CSS for layout just word-wraps long labels and keeps everything aligned, which I think is better.&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Allow posts to be rated?&#039;&#039;&#039; I would put the disabled controls in a group that only appears if the checkbox is checked, so that those controls show and hide, rather than enabling or disabling. However, if we want to keep this the same as it is now, you could implemnt the conditionalenable group type.&lt;br /&gt;
&lt;br /&gt;
===Manage groups for a course===&lt;br /&gt;
&lt;br /&gt;
This sort of form is currently beyond what the form library was designed to produce. I would leave this as hand-coded HTML. In time, the form library may gain some AJAX field types for selecting students, and such like, that could usefully be within a redesigned version of this form. Since the form field types just output HTML and Javascript, it should be possible to use them within hand-crafted forms.&lt;br /&gt;
&lt;br /&gt;
[[Image:Formproposal_Groups_form.png]]&lt;br /&gt;
&lt;br /&gt;
==Questions==&lt;br /&gt;
&lt;br /&gt;
Do we need a syntax like lable=&#039;langfile:langstring&#039; for using lang strings from other lang files where necessary? &#039;&#039;&#039;yes&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Where does this live. I think the main library file that people have to include should be lib/formlib.php, and that is all anyone needs to include. However, to we break each field type into its own PHP file, perhaps in a lib/formlib directory, to make it easier to add new field types in future? &#039;&#039;&#039;Probably all in one file&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Some form fields will need bits of CSS and JavaScript to work. Do we add the CSS to the standard theme, and combine all the javascript into a single library somewhere, or do we break it up into the individual field types, and recombine it dynamically at runtime? I think I favour breaking up the PHP code, but keeping all the JS and CSS in one place. &#039;&#039;&#039;Should use YUI were possible. We will still need some custom JavaScript and CSS though. Where?&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
Do we use this for vaidation as well? &#039;&#039;&#039;Yes.&#039;&#039;&#039; Currently Moodle PHP files that do forms tend to look like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$var1 = require/optional_param(...);&lt;br /&gt;
// etc. for all the other form variables.&lt;br /&gt;
&lt;br /&gt;
if (data_submitted &amp;amp;&amp;amp; confirm_sesskey()) {&lt;br /&gt;
   // Try to process submitted data.&lt;br /&gt;
   if (/*processing ok*/)&lt;br /&gt;
	   // Redirect away&lt;br /&gt;
} else {&lt;br /&gt;
   // Set initial form values.&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
// Lots of code to output the form HTML. Of maybe include(somthing.html);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The proposal is to change this to something like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$mf = new moodle_form();&lt;br /&gt;
// Setup $mf with the form structure&lt;br /&gt;
&lt;br /&gt;
$form = new stdClass;&lt;br /&gt;
if ($mf-&amp;gt;validate_submitted_data($form)) { // Pass by reference to get data back.&lt;br /&gt;
   // Try to process submitted data in $form&lt;br /&gt;
   if (/*processing ok*/)&lt;br /&gt;
	   // Redirect away&lt;br /&gt;
} else {&lt;br /&gt;
   // Set initial values in $form&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
$mf-&amp;gt;display($form)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer|Obsolete - Moodle forms library]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Category:Obsolete_Design&amp;diff=45108</id>
		<title>Category:Obsolete Design</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Category:Obsolete_Design&amp;diff=45108"/>
		<updated>2008-10-10T14:13:47Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: New page: These articles were created to assist developers in the creation of new Moodle features. Once the code is written and added to CVS, these articles are less and less used and updated, so th...&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;These articles were created to assist developers in the creation of new Moodle features. Once the code is written and added to CVS, these articles are less and less used and updated, so their content should not be trusted, especially their API and DB schema specifications.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Grades&amp;diff=45106</id>
		<title>Development:Grades</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Grades&amp;diff=45106"/>
		<updated>2008-10-10T14:11:56Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Flagging this article as Obsolete design&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[http://moodle.org/mod/forum/discuss.php?d=69223 There is an ongoing discussion about this spec here]&lt;br /&gt;
{{obsolete_design}}&lt;br /&gt;
== Executive Summary ==&lt;br /&gt;
&lt;br /&gt;
The gradebook mechanisms must be rebuilt to:&lt;br /&gt;
&lt;br /&gt;
# &#039;&#039;&#039;Improve performance and scalability&#039;&#039;&#039; - All grades from throughout the system will be pushed to a central system of tables. This means reports based on grades can be generated much faster, and the gradebook has ultimate control over the content.&lt;br /&gt;
# &#039;&#039;&#039;Improve flexibility&#039;&#039;&#039; - All aspects of the new gradebook will use simple plugin structures, namely: exports, imports and displays/reports. It is expected that the community will be very active in producing [[Development:Gradebook Report Tutorial |special-purpose reports]] analysing the basic grade data in new ways, for example, or writing plugins to transfer grades to student information systems.&lt;br /&gt;
# &#039;&#039;&#039;Allow rubrics for outcomes (aka standards,competencies,goals)&#039;&#039;&#039; - As well as numerical grades, each grading item can consist of a number of scores made on a rubric against a standard outcome statement. These can be automatically converted to a numerical grade if desired or just shown as is.&lt;br /&gt;
# &#039;&#039;&#039;Allow arbitrary columns and derived columns&#039;&#039;&#039; - Arbitrary columns of data can be added (either manually or via import). Columns can also be automatically filled based on formulas.&lt;br /&gt;
# &#039;&#039;&#039;Implement limited public API&#039;&#039;&#039; - Activities may use this API to send grades/outcomes to gradebook and find out the final grades.&lt;br /&gt;
&lt;br /&gt;
== Glossary ==&lt;br /&gt;
&lt;br /&gt;
Here are some terms used in the gradebook, both in the development and the user interface.  Using these terms in discussions about the gradebook will help to reduce confusion.&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;Term&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Definition&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|Activity&lt;br /&gt;
|An instance of an activity module [[Category:Modules|Module]] (e.g. a single quiz, assignment etc...)&lt;br /&gt;
|-&lt;br /&gt;
|Calculation&lt;br /&gt;
|A formula used to calculate grades, based (optionally) on other grade items. Not the same as [[Calculated_question_type|Calculated question types]].&lt;br /&gt;
|-&lt;br /&gt;
|Category&lt;br /&gt;
|A set of Grade Items.  A Category also has its own aggregated Grade which is calculated from its Grade Items.  There is no limit to the level of nesting of Categories (a Category may belong to another Category). However, each Grade Item may belong to only one Category. &lt;br /&gt;
|-&lt;br /&gt;
|Course completion&lt;br /&gt;
|The concept of meeting certain criteria for completing a course. In the context of the gradebook, this means a set of grades that must be reached, or a number of outcomes/competencies to complete/master.&lt;br /&gt;
|-&lt;br /&gt;
|Grade&lt;br /&gt;
|A Grade is a single assessment. It may be a number or an item on a scale (possibly tied to an Outcome). Raw grade value is the numerical or scale grade from activity. Final grade is the grade reported in gradebook.&lt;br /&gt;
|-&lt;br /&gt;
|[[Gradebook|Gradebook]]&lt;br /&gt;
|A central location in Moodle where students&#039; Grades are stored and displayed. Teachers can keep track of their students&#039; progress and organise which set of Grades their students will be able to see. Students see their own Grades.&lt;br /&gt;
|-&lt;br /&gt;
|Grade Item&lt;br /&gt;
|A &amp;quot;column&amp;quot; of Grades.  It can be created from a specific Activity or other module, calculated from other Grade Items, or entered manually.&lt;br /&gt;
|-&lt;br /&gt;
|[[Development:Grades#Locked_grades|Grade Locks]]&lt;br /&gt;
|See linked section of this page&lt;br /&gt;
|-&lt;br /&gt;
|History&lt;br /&gt;
|The gradebook has its own type of log, which keeps a History of all changes made to grades.&lt;br /&gt;
|-&lt;br /&gt;
|[[Development:Outcomes|Outcome]]&lt;br /&gt;
|[[Development:Outcomes|Outcomes]] are specific descriptions of what a person is expected to be able to do or understand at the completion of an activity or course. An activity might have more than one outcome, and each may have a grade against it (usually on a scale).  Other terms for Outcomes are &#039;&#039;Competencies&#039;&#039; and &#039;&#039;Goals&#039;&#039;. See some [[Development:Outcomes_examples|Examples]].&lt;br /&gt;
|-&lt;br /&gt;
|[[Scales|Scale]]&lt;br /&gt;
|A scale is a set of responses from which the teacher can choose one.   eg   Very cool, Cool, Fairly cool, Not very cool, Not cool&lt;br /&gt;
|-&lt;br /&gt;
|Letter Grades&lt;br /&gt;
|Special representation of grade values similar to scales.  Letters are configured in course contexts or above and are defined by lower boundary.   eg   A (above 90 %), B (above 80 %), C (above 70 %), D (above 50 %), F (above 0 %)&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Database structures ==&lt;br /&gt;
=== grade_items ===&lt;br /&gt;
&lt;br /&gt;
This table keeps information about gradeable items (ie columns). If an activity (eg an assignment or quiz) has multiple grade_items associated with it (eg several outcomes and numerical grade), then there will be a corresponding multiple number of rows in this table.&lt;br /&gt;
&lt;br /&gt;
idnumber is a tag unique inside a course identifying the grade item, useful for identifying data in exports and for referring to the grade item in calculations.  It is the same as the idnumber in course_modules.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;courseid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|The course this item is part of &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;categoryid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|the category group this item belongs to &lt;br /&gt;
|-&lt;br /&gt;
|itemname &lt;br /&gt;
|varchar(255) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|The name of this item (pushed in by the module or entered by user) &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;itemtype&#039;&#039;&#039; &lt;br /&gt;
|varchar(30) &lt;br /&gt;
|&lt;br /&gt;
|&#039;mod&#039;, &#039;blocks&#039;, &#039;manual&#039;, &#039;course&#039;, &#039;category&#039; etc &lt;br /&gt;
|-&lt;br /&gt;
|itemmodule  &lt;br /&gt;
|varchar(30)  &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|&#039;forum&#039;, &#039;quiz&#039;, &#039;csv&#039;, etc &lt;br /&gt;
|-&lt;br /&gt;
|iteminstance &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|id of the item module &lt;br /&gt;
|-&lt;br /&gt;
|itemnumber &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|Can be used to distinguish multiple grades for an activity &lt;br /&gt;
|-&lt;br /&gt;
|iteminfo &lt;br /&gt;
|text &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|Info and notes about this item XXX &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;idnumber&#039;&#039;&#039;&lt;br /&gt;
|varchar(255) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|Arbitrary idnumber provided by the module responsible (optional and course unique)&lt;br /&gt;
|-&lt;br /&gt;
|calculation &lt;br /&gt;
|text&lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|Spreadsheet-type formula used to process the raw grades into final grades&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;gradetype&#039;&#039;&#039; &lt;br /&gt;
|int(4) &lt;br /&gt;
|&amp;lt;center&amp;gt;1&amp;lt;/center&amp;gt; &lt;br /&gt;
|0 = none, 1 = value, 2 = scale, 3 = text &lt;br /&gt;
|-&lt;br /&gt;
|grademax &lt;br /&gt;
|float(10,5) &lt;br /&gt;
|&amp;lt;center&amp;gt;100&amp;lt;/center&amp;gt; &lt;br /&gt;
|What is the maximum allowable grade? &lt;br /&gt;
|-&lt;br /&gt;
|grademin &lt;br /&gt;
|float(10,5) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|What is the minimum allowable grade? &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;scaleid&#039;&#039;&#039; &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|If this grade is based on a scale, which one is it? &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;outcomeid&#039;&#039;&#039; &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|If this is outcome item, which outcome is it? &lt;br /&gt;
|-&lt;br /&gt;
|gradepass&lt;br /&gt;
|float(10,5) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|What grade is needed to pass?  grademin &amp;lt;= gradepass &amp;lt;= grademax&lt;br /&gt;
|-&lt;br /&gt;
|multfactor &lt;br /&gt;
|float(10,5) &lt;br /&gt;
|&amp;lt;center&amp;gt;1.0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Multiply all raw grades from activities by this &lt;br /&gt;
|-&lt;br /&gt;
|plusfactor  &lt;br /&gt;
|float(10,5) &lt;br /&gt;
|&amp;lt;center&amp;gt;0.0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Add this to all raw grades from activities by this &lt;br /&gt;
|-&lt;br /&gt;
|aggregationcoef  &lt;br /&gt;
|float(10,5) &lt;br /&gt;
|&amp;lt;center&amp;gt;0.0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Weight applied to all grades in this grade item during aggregation with other grade items.&lt;br /&gt;
|-&lt;br /&gt;
|sortorder &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Sorting order of the columns (pre-order walk of the grading tree)&lt;br /&gt;
|-&lt;br /&gt;
|display&lt;br /&gt;
|int(10)  &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Display as real grades, percentages (in reference to the minimum and maximum grades) or letters (A, B, C etc..), or course default (0)&lt;br /&gt;
|-&lt;br /&gt;
|hidden &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|1 is hidden, 1 is hide always, &amp;gt; 1 is a date to hide until (prevents viewing of all user grades) &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;locked&#039;&#039;&#039;&lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|0 is not locked, &amp;gt; 0 is a date when was item locked (no final grade or grade_item updates possible)&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;locktime&#039;&#039;&#039;&lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|0 no auto locking, &amp;gt; 0 is a date to lock grade item and final grades after automatically &lt;br /&gt;
|-&lt;br /&gt;
|deleted &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|1 means the associated module instance has been deleted&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;needsupdate&#039;&#039;&#039;&lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|If this flag is set, then the whole column will be recalculated. If set in course item, some other item needs recalculation. Calculated and category items are recalculated together with any other items.&lt;br /&gt;
|-&lt;br /&gt;
|timecreated &lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The first time this grade_item was created&lt;br /&gt;
|-&lt;br /&gt;
|timemodified &lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The last time this grade_item was modified&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== grade_categories ===&lt;br /&gt;
&lt;br /&gt;
This table keeps information about categories, used for grouping items.  An associated grade_item will be maintained for each category to store the aggregate data.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;courseid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|The course this grade category is part of &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;parent&#039;&#039;&#039; &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|Parent grade_category (hierarchical)&lt;br /&gt;
|-&lt;br /&gt;
|depth&lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|How deep is this category from the highest level (1,2,3)&lt;br /&gt;
|-&lt;br /&gt;
|path&lt;br /&gt;
|varchar(255) &lt;br /&gt;
| &lt;br /&gt;
|Shows the path as /1/2/3/  &lt;br /&gt;
|-&lt;br /&gt;
|fullname &lt;br /&gt;
|varchar(255) &lt;br /&gt;
|&lt;br /&gt;
|The name of this grade category &lt;br /&gt;
|-&lt;br /&gt;
|aggregation &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|A constant pointing to one of the predefined aggregation strategies (none, mean,median,sum, etc) &lt;br /&gt;
|-&lt;br /&gt;
|keephigh &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Keep only the X highest items &lt;br /&gt;
|-&lt;br /&gt;
|droplow &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Drop the X lowest items &lt;br /&gt;
|-&lt;br /&gt;
|aggregateonlygraded&lt;br /&gt;
|int(1) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Aggregate only existing grades &lt;br /&gt;
|-&lt;br /&gt;
|aggregateoutcomes&lt;br /&gt;
|int(1) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Aggregate otcomes together with normal items &lt;br /&gt;
|-&lt;br /&gt;
|aggregatesubcats&lt;br /&gt;
|int(1) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Aggregate only items placed directly in category or all items in subcategories excluding the subcategory totals &lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) &lt;br /&gt;
|&lt;br /&gt;
|The first time this grade_category was created&lt;br /&gt;
|-&lt;br /&gt;
|timemodified &lt;br /&gt;
|int(10) &lt;br /&gt;
|&lt;br /&gt;
|The last time this grade_category was modified&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== grade_grades ===&lt;br /&gt;
&lt;br /&gt;
This table keeps individual grades for each user and each item.  The raw grade is exactly as imported or submitted by modules. The rawgrademax/min and rawscaleid are stored here to record the values at the time the grade was stored, because teachers might change this for an activity!   All the results are normalised/resampled/calculated for the finalgrade, which is relative to the max/min/scaleid values stored in the grade_item.  The finalgrade field is effectively a cache and values are rebuilt whenever raw values or the grade_item changes.&lt;br /&gt;
&lt;br /&gt;
Note that the finalgrade for a scale-based item may be non-integer!  It needs to be rounded on display.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;itemid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|The item this grade belongs to &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;userid&#039;&#039;&#039; &lt;br /&gt;
|int(10) &lt;br /&gt;
|&lt;br /&gt;
|The user who this grade is for &lt;br /&gt;
|-&lt;br /&gt;
|rawgrade&lt;br /&gt;
|float(11,10) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|The raw grade that came into the system&lt;br /&gt;
|-&lt;br /&gt;
|rawgrademax &lt;br /&gt;
|float(11,10) &lt;br /&gt;
|&amp;lt;center&amp;gt;100&amp;lt;/center&amp;gt; &lt;br /&gt;
|The maximum allowable grade when this was created &lt;br /&gt;
|-&lt;br /&gt;
|rawgrademin &lt;br /&gt;
|float(11,10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|The minimum allowable grade when this was created &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;rawscaleid&#039;&#039;&#039; &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|If this grade is based on a scale, which one was it? &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;usermodified&#039;&#039;&#039;&lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|the userid of the person who last modified the raw grade value&lt;br /&gt;
|-&lt;br /&gt;
|finalgrade&lt;br /&gt;
|float(11,10) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|The final grade (cached) after all calculations are made&lt;br /&gt;
|-	 &lt;br /&gt;
|hidden &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|0 is not hidden, 1 is hide always, &amp;gt; 1 is a date to hide until &lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;locked&#039;&#039;&#039;&lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|0 is not locked, &amp;gt; 0 when was the grade locked&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;locktime&#039;&#039;&#039;&lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|0 is never, &amp;gt; 0 is a date to lock the final grade after automatically&lt;br /&gt;
|-&lt;br /&gt;
|exported &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|0 is not exported, &amp;gt; 0 is the last exported date &lt;br /&gt;
|-&lt;br /&gt;
|excluded &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|grade excluded from aggregation, &amp;gt; 0 is the last exported date &lt;br /&gt;
|-&lt;br /&gt;
|overridden &lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|0 is not overridden, &amp;gt; 0 is the last overridden date &lt;br /&gt;
|-&lt;br /&gt;
|feedback &lt;br /&gt;
|text &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|Manual feedback from the teacher. Could be a code like &#039;mi&#039;. &lt;br /&gt;
|-&lt;br /&gt;
|feedbackformat&lt;br /&gt;
|int(10)&lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Text format for feedback&lt;br /&gt;
|-&lt;br /&gt;
|information &lt;br /&gt;
|text &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|not sued yet (Further information like forum rating distribution 4/5/7/0/1 ?)&lt;br /&gt;
|-&lt;br /&gt;
|informationformat&lt;br /&gt;
|int(10)&lt;br /&gt;
|&amp;lt;center&amp;gt;0&amp;lt;/center&amp;gt; &lt;br /&gt;
|Text format for information&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) &lt;br /&gt;
|&lt;br /&gt;
|temporary hack - the date of submission in activity if any, new field expected in 2.0&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10) &lt;br /&gt;
|&lt;br /&gt;
|temporary hack - the date of grading in activity or date of manual grading in gradebook, new field expected in 2.0&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== grade_outcomes ===&lt;br /&gt;
&lt;br /&gt;
This table describes the outcomes used in the system. An outcome is a statement tied to a rubric scale from low to high, such as “Not met, Borderline, Met” (stored as 0,1 or 2).  For more info about these see [[Development:Outcomes]].&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;courseid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|Mostly these are defined site wide ie NULL &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|shortname &lt;br /&gt;
|varchar(255) &lt;br /&gt;
|&lt;br /&gt;
|The short name or code for this outcome statement &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|fullname &lt;br /&gt;
|text &lt;br /&gt;
|&lt;br /&gt;
|The full description of the outcome (usually 1 sentence) &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;scaleid&#039;&#039;&#039; &lt;br /&gt;
|int(10) &lt;br /&gt;
|&lt;br /&gt;
|The recommended scale for this outcome.  &lt;br /&gt;
|-&lt;br /&gt;
|description &lt;br /&gt;
|text &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|The full description of the outcome (usually 1 sentence) &lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10) &lt;br /&gt;
|&lt;br /&gt;
|the time this outcome was first created &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10) &lt;br /&gt;
|&lt;br /&gt;
|the time this outcome was last updated &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;usermodified&#039;&#039;&#039;&lt;br /&gt;
|int(10) &lt;br /&gt;
|&amp;lt;center&amp;gt;NULL&amp;lt;/center&amp;gt; &lt;br /&gt;
|the userid of the person who last modified this outcome&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== grade_outcomes_courses ===&lt;br /&gt;
An intersection table used to make standard outcomes available to courses.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;courseid&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The id of the course being assigned the outcome&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;outcomeid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|The id of the outcome being assigned to the course&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== grade_import_newitem ===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|itemname&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|*TODO* Document&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|importcode&lt;br /&gt;
|int(12)  &lt;br /&gt;
|&lt;br /&gt;
|*TODO* Document &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== grade_import_values ===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;itemid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|NULL&lt;br /&gt;
|*TODO* Document &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|newgradeitem&lt;br /&gt;
|int(10)&lt;br /&gt;
|NULL&lt;br /&gt;
|*TODO* Document&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;userid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|*TODO* Document &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|finalgrade&lt;br /&gt;
|float(10,5)  &lt;br /&gt;
|NULL&lt;br /&gt;
|*TODO* Document &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|feedback&lt;br /&gt;
|text&lt;br /&gt;
|NULL&lt;br /&gt;
|*TODO* Document &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|importcode&lt;br /&gt;
|int(12)  &lt;br /&gt;
|&lt;br /&gt;
|*TODO* Document &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== History tables ===&lt;br /&gt;
&lt;br /&gt;
These table keep track of changes to most of the grade tables. Using these it should be possible to reconstruct the grades at any point in time in the past, or to audit grade changes over time.  It should be quicker to use these tables for that, rather than storing this information in the main Moodle log. The following tables are set up for that purpose:&lt;br /&gt;
&lt;br /&gt;
#grade_categories_history&lt;br /&gt;
#grade_grades_history&lt;br /&gt;
#grade_items_history&lt;br /&gt;
#grade_outcomes_history&lt;br /&gt;
&lt;br /&gt;
Each of them has exactly the same DB structure as their matching table (e.g. grade_categories), with 3 extra fields:&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|action&lt;br /&gt;
|int(10)  &lt;br /&gt;
|0&lt;br /&gt;
|The action that lead to the change being recorded (insert, update, delete)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;oldid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|The id of the record being changed or inserted (PK of the main table, not the history table) &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|source&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|NULL&lt;br /&gt;
|The URL from which the action originated &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== grade_letters ===&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|contextid&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|What contextid does this letter apply to (from levels CONTEXT_SYSTEM, CONTEXT_COURSECAT or CONTEXT_COURSE)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|lowerboundary&lt;br /&gt;
|float(10,5)  &lt;br /&gt;
|&lt;br /&gt;
|The lower boundary of the letter. Its upper boundary is the lower boundary of the next highest letter, unless there is none above, in which case it&#039;s grademax for that grade_item.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|letter&lt;br /&gt;
|varchar(255)  &lt;br /&gt;
|&lt;br /&gt;
|The display value of the letter. Can be any character or string of characters (OK, A, 10% etc..) &lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
== Overview of module communication ==&lt;br /&gt;
&lt;br /&gt;
Modules usually store raw grades internally and pass them into gradebook every time they change. Gradebook may also request activities to resend the grades. &lt;br /&gt;
&lt;br /&gt;
The gradebook is designed to be as separate as possible from the code of activities - modules do not read grade tables or use internal gradebook API. &lt;br /&gt;
&lt;br /&gt;
Originally it was planned to use new events API, but in the end it was decided to use minimal API consisting of several function in lib/gradelib.php and each mod/xxx/lib.php&lt;br /&gt;
&lt;br /&gt;
=== Backward compatibility with Moodle 1.8 and earlier ===&lt;br /&gt;
&lt;br /&gt;
Function grade_grab_legacy_grades($courseid) may be used to request transfer of grades from legacy or 3rd party activities which were not yet converted to new grade API. This function is not called automatically.&lt;br /&gt;
&lt;br /&gt;
Modules are responsible to push existing grades into gradebook during upgrade.&lt;br /&gt;
&lt;br /&gt;
==API for communication with modules/blocks==&lt;br /&gt;
&lt;br /&gt;
Modules may use only functions from lib/gradelib.php which are marked as public. This API may be extended in later 1.9.x release. Activities should access/update only own grades.&lt;br /&gt;
&lt;br /&gt;
===grade_get_grades()===&lt;br /&gt;
&lt;br /&gt;
grade_get_grades($courseid, $itemtype, $itemmodule, $iteminstance, $userid_or_ids=0)&lt;br /&gt;
&lt;br /&gt;
Returns grading information for given activity - optionally with users grades. Manual, course or category items can not be queried.&lt;br /&gt;
&lt;br /&gt;
===grade_get_outcomes()===&lt;br /&gt;
&lt;br /&gt;
grade_get_outcomes($courseid, $itemtype, $itemmodule, $iteminstance,$userid=0)&lt;br /&gt;
&lt;br /&gt;
Returns list of outcomes used in course together with current outcomes for this user.&lt;br /&gt;
&lt;br /&gt;
===grade_is_locked()===&lt;br /&gt;
&lt;br /&gt;
eg grade_is_locked($courseid, $itemtype, $itemmodule, $iteminstance, $itemnumber, $userid=NULL)&lt;br /&gt;
&lt;br /&gt;
This function will tell a module whether a grade (or grade_item if $userid is not given) is currently locked or not. If it&#039;s locked to the current user then the module can print a nice message or prevent editing in the module. If no $userid is given, the method will always return the grade_item&#039;s locked state. If a $userid is given, the method will first check the grade_item&#039;s locked state (the column). If it is locked, the method will return true no matter the locked state of the specific grade being checked. If unlocked, it will return the locked state of the specific grade.&lt;br /&gt;
([http://moodle.org/mod/forum/discuss.php?d=69223#p311329 info])&lt;br /&gt;
&lt;br /&gt;
===grade_update()===&lt;br /&gt;
&lt;br /&gt;
grade_update($source, $courseid, $itemtype, $itemmodule, $iteminstance, $itemnumber, $grades=NULL, $itemdetails=NULL)&lt;br /&gt;
&lt;br /&gt;
Submit new or update grade; update/create grade_item definition. Grade must have userid specified, rawgrade and feedback with format are optional. rawgrade NULL means &#039;Not graded&#039;, missing property or key means do not change existing. Only following grade item properties can be changed &#039;itemname&#039;, &#039;idnumber&#039;, &#039;gradetype&#039;, &#039;grademax&#039;, &#039;grademin&#039;, &#039;scaleid&#039;, &#039;multfactor&#039;, &#039;plusfactor&#039;, &#039;deleted&#039;.&lt;br /&gt;
&lt;br /&gt;
===grade_update_outcomes()===&lt;br /&gt;
&lt;br /&gt;
grade_update_outcomes($source, $courseid, $itemtype, $itemmodule, $iteminstance, $userid, $data)&lt;br /&gt;
&lt;br /&gt;
Updates outcomes of a given user. Manual outcomes cannot be updated.&lt;br /&gt;
&lt;br /&gt;
== Private gradebook API ==&lt;br /&gt;
Private API is used by gradebook plugins and core Moodle code, it may change in 2.0. Most  of the interesting classes and functions are in lib/gradelib.php, grade/lib.php and grade/report/lib.php.&lt;br /&gt;
&lt;br /&gt;
===grade_regrade_final_grades()===&lt;br /&gt;
&lt;br /&gt;
grade_regrade_final_grades($courseid=NULL, $userid=NULL, $updated_item=NULL)&lt;br /&gt;
&lt;br /&gt;
Updates all grade_grades-&amp;gt;finalgrade records for each grade_item matching the given attributes. The search is further restricted, so that only grade_items that have needs_update == true or that use calculation are retrieved and used for the update. The function returns the number of grade_items updated (NOT the same as the number of grades_grades updated!).&lt;br /&gt;
&lt;br /&gt;
===grade_verify_idnumber()===&lt;br /&gt;
&lt;br /&gt;
grade_verify_idnumber($idnumber, $grade_item = null, $cm = null, $gradeitem)&lt;br /&gt;
&lt;br /&gt;
Verify new value of idnumber - checks for uniqueness of new idnubmers, existing are kept intact.&lt;br /&gt;
&lt;br /&gt;
===remove_course_grades()===&lt;br /&gt;
&lt;br /&gt;
remove_course_grades($courseid, $showfeedback)&lt;br /&gt;
&lt;br /&gt;
Remove all grade related course data - history is kept&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
TODO: add description of the other methods and classes + simple usage examples + querylib.php description&lt;br /&gt;
&lt;br /&gt;
== Dealing with multiple grades  ==&lt;br /&gt;
&lt;br /&gt;
Modules usually produce only one grade item per activity. Optionally one or more outcomes may be attached to activities.&lt;br /&gt;
&lt;br /&gt;
Some activities may need to aggregate multiple ratings or attempts before sending them into the gradebook. Activities can not send variable number of items.&lt;br /&gt;
&lt;br /&gt;
If the gradebook receives multiple grade items from a module, then they are automatically grouped together in a unique grade category (with the same name as the module instance). See [[Development:Outcomes]] for more details.&lt;br /&gt;
&lt;br /&gt;
TODO: this may still be changed&lt;br /&gt;
&lt;br /&gt;
== Calculated grade items  ==&lt;br /&gt;
&lt;br /&gt;
Categories or manual items maybe calculated using spreadsheet-like formulas.  Formulas may reference other items from the same course only using Id numbers in double square brackets.&lt;br /&gt;
&lt;br /&gt;
 eg:  &amp;lt;nowiki&amp;gt;= MEAN([[quiz121]], [[quizend]]) + [[assignmentAXC]] + 20.0&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Regrading / updating of final grades ==&lt;br /&gt;
&lt;br /&gt;
TODO: describe needsupdate flag and incremental updates&lt;br /&gt;
&lt;br /&gt;
== Adjustment of raw grades ==&lt;br /&gt;
Grade_item contains optional rules for adjusting the raw grade before it is cached into a final grade. These rules are processed BEFORE the calculation discussed above. Scale is never changed. Multfactor and plusfactor may be used to alter raw grades coming from activities, but it is recommended to use formulas instead.&lt;br /&gt;
&lt;br /&gt;
== Displaying the grades to ordinary participants  ==&lt;br /&gt;
&lt;br /&gt;
The module takes responsibility for displaying grades within the module (to a student, say). It is recommended to use the real final grades obtained using grade_get_grades() functionBecause guidebook might force hiding, override grade, etc.&lt;br /&gt;
&lt;br /&gt;
For full display of grades in a whole course say, the student uses the same link as teachers use to access the gradebook. However, due to their different permissions they will only have access to specific reports. By default this is the &#039;&#039;User report&#039;&#039; report which only shows their own grades and has very few configuration options.&lt;br /&gt;
&lt;br /&gt;
== Locked grades  ==&lt;br /&gt;
&lt;br /&gt;
Both whole columns and individual grades can be locked in the gradebook, via the &#039;&#039;locked&#039;&#039; field.  Teachers may want to do this to prevent further changes from the modules, or from other teachers.  When a grade is locked, any changes that might affect that grade are ignored.  When the graded is unlocked, activities are asked to resend the latest grades.&lt;br /&gt;
&lt;br /&gt;
In the main GUIs the lock toggling will be achieved by clicking on a little padlock icon beside each entry or column.&lt;br /&gt;
&lt;br /&gt;
There is also an option to lock grade or item after some specified date.&lt;br /&gt;
&lt;br /&gt;
== Overridden grades ==&lt;br /&gt;
&lt;br /&gt;
Grades can be manually modified (overridden) in the gradebook.  When this is done the entered value is always used instead of the aggregated, calculated or activity grade.&lt;br /&gt;
&lt;br /&gt;
== Logging ==&lt;br /&gt;
&lt;br /&gt;
All grading related changes maybe logged in history tables.&lt;br /&gt;
&lt;br /&gt;
== Security Issues ==&lt;br /&gt;
&lt;br /&gt;
For security an option to force SSL for the gradebook might be good.&lt;br /&gt;
&lt;br /&gt;
==Overall grade==&lt;br /&gt;
&lt;br /&gt;
Each course has exactly one course grade item. It may be used for this purpose now. Other course completion criteria will be implemented in 2.0.&lt;br /&gt;
&lt;br /&gt;
== Report plugins ==&lt;br /&gt;
All the main interface of the gradebook are implemented as report plugins. Each plugin is fully responsible for page layout, there are some handy functions in grade/lib.php. They can even define their own capabilities and extra tables if the core tables are not enough, as they&#039;ll have a full /grade/report/xxxx/db directory.&lt;br /&gt;
&lt;br /&gt;
Each report defines one capability to allow people to see that report, so that admins have control over who can see what reports. For example, the participant interface can be a totally separate report plugin.&lt;br /&gt;
&lt;br /&gt;
This allows for the widest flexibility and safety in how grades are presented.&lt;br /&gt;
&lt;br /&gt;
=== Default teacher interface ===&lt;br /&gt;
This interface will be what teachers see by default, and will subsume everything the current interface (in Moodle 1.8) does.&lt;br /&gt;
&lt;br /&gt;
Some snippets of functionality:&lt;br /&gt;
{{Moodle 1.9}}&lt;br /&gt;
Overall it&#039;s a grid, with participant names down one side and grade items along the top. &lt;br /&gt;
&lt;br /&gt;
Columns will be able to be collapsed together by grouping them into categories. Grades for categories can be calculated via various means. &lt;br /&gt;
&lt;br /&gt;
“Eye-cons” on the columns and checkboxes by every grade (this bit possibly controlled with a switch) allow hiding by category, by column, by individual grade.&lt;br /&gt;
&lt;br /&gt;
Textual notes can be added to each grade for more info. These show up to participants as well.&lt;br /&gt;
&lt;br /&gt;
A groups menu allows the teacher to switch between showing EACH of the groups they have access to, or ALL the groups they have access to.&lt;br /&gt;
&lt;br /&gt;
All grade items will link to modulepath/grade.php?id=44 which will work out what the current person should be allowed to see and either redirect them to the correct page or just show them immediately.   This copes with situations like the quiz, say, where we want editing teachers to go to the detailed reports there while participants just see their own grade or whatever the quiz is set to show.&lt;br /&gt;
&lt;br /&gt;
User preference to SWITCH between showing raw grades, percentage grades, or both, or grade letters (A/B/C etc).  &lt;br /&gt;
&lt;br /&gt;
Settings for grade letters not only define the transformation from percentage to grades, but also the transformation from letters to grades (in case the teacher edits some of the letter grades).&lt;br /&gt;
&lt;br /&gt;
Categories are shown above the headings for each column.  Clicking for more info on a category will just show the category with a summary column showing total/average for just that category (PLUS the summary column for the whole course).&lt;br /&gt;
&lt;br /&gt;
All columns should be sortable up/down.&lt;br /&gt;
&lt;br /&gt;
At the bottom of each column is a row with the mean course score.  If in groups mode, then add ANOTHER row with just the group mean. Add the number of grades used in brackets.  eg 56% (11).   When the report is paged, these means are still for the whole course/group (not the page!)&lt;br /&gt;
&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
&lt;br /&gt;
Teachers can type “straight into” the grid using AJAX or fallback to forms. No popup menus for values.&lt;br /&gt;
&lt;br /&gt;
Later on we can support customisable shorthand codes to make data entry quick (eg type &#039;ab&#039; for absent, or &#039;nge&#039; for not good enough).&lt;br /&gt;
&lt;br /&gt;
See [http://test.moodle.com/grade/report/grader/index.php?id=2 the test site] for a live demo of this report.&lt;br /&gt;
&lt;br /&gt;
=== Default participant interface ===&lt;br /&gt;
This interface will be what participants see by default:&lt;br /&gt;
&lt;br /&gt;
Some snippets of functionality:&lt;br /&gt;
&lt;br /&gt;
*Invert the grid to show one item per row, with the total/average at the bottom.&lt;br /&gt;
*Use second/third columns to show categories.&lt;br /&gt;
*Include ranking score in another column.&lt;br /&gt;
*Show feedback&lt;br /&gt;
*Show percentage&lt;br /&gt;
*No editing functionality.&lt;br /&gt;
&lt;br /&gt;
See [http://test.moodle.com/grade/report/user/index.php?id=2 the test site] for a live demo of this report.&lt;br /&gt;
&lt;br /&gt;
=== Outcomes report ===&lt;br /&gt;
This simple informational report displays all the outcomes used by the course, with the following information:&lt;br /&gt;
&lt;br /&gt;
*Outcome name&lt;br /&gt;
*Overall average: If the outcome is used by more than one activity, this shows you the mean across all these activities in the current course&lt;br /&gt;
*Site-wide: Yes or No: A site-wide outcome is automatically made available to all courses.&lt;br /&gt;
*Activities: A list of links to the activities in the current course that use each outcome. One row per activity (table splits here)&lt;br /&gt;
*Average: For each activity using the outcome, the average score is shown.&lt;br /&gt;
*Number of grades: For each activity using the outcome, the number of grades is shown (non-graded participants are ignored)&lt;br /&gt;
&lt;br /&gt;
See [http://test.moodle.com/grade/report/outcomes/index.php?id=2 the test site] for a live demo of this report.&lt;br /&gt;
&lt;br /&gt;
=== Overview report ===&lt;br /&gt;
Another basic report, showing a participant&#039;s course averages in each of the courses in which s/he has received grades.&lt;br /&gt;
&lt;br /&gt;
See [http://test.moodle.com/grade/report/overview/index.php the test site] for a live demo of this report.&lt;br /&gt;
&lt;br /&gt;
== Export plugins ==&lt;br /&gt;
&lt;br /&gt;
The API for these is extremely simple.  Each export plugin should occupy a directory under /grade/export/xyz and needs to provide only an index.php file as a the primary interface. This file just accepts a &#039;courseid&#039; parameter.&lt;br /&gt;
&lt;br /&gt;
== Import plugins ==&lt;br /&gt;
&lt;br /&gt;
Each import plugin should occupy a directory under /grade/import/xyz and needs to provide only an index.php file as a the primary interface.  This file just accepts a &#039;courseid&#039; parameter.&lt;br /&gt;
&lt;br /&gt;
The index.php will show an interface for further options and selections.&lt;br /&gt;
&lt;br /&gt;
Import plugin must validate data before starting the import operation, if some parts of import fail the user must be notified.&lt;br /&gt;
&lt;br /&gt;
Some sample import plugins are:&lt;br /&gt;
&lt;br /&gt;
===Import from CSV===&lt;br /&gt;
&lt;br /&gt;
Accepts an upload of (or URL to) a CSV file.  Multiple options describe how to process the file, which columns to add etc.  The imported grades always override current grades.&lt;br /&gt;
&lt;br /&gt;
===Import from XML===&lt;br /&gt;
&lt;br /&gt;
Accepts an upload of (or URL to) an XML file with this kind of format (from OU). &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;results batch=&amp;quot;[someuniqueimportnumber]&amp;quot;&amp;gt;&lt;br /&gt;
     &amp;lt;result&amp;gt;&lt;br /&gt;
         &amp;lt;state&amp;gt;[&#039;new&#039; or &#039;regrade&#039;]&amp;lt;/state&amp;gt;&lt;br /&gt;
             &amp;lt;assignment&amp;gt;[idnumber]&amp;lt;/assignment&amp;gt;&lt;br /&gt;
             &amp;lt;student&amp;gt;[studentid]&amp;lt;/student&amp;gt;&lt;br /&gt;
             &amp;lt;score&amp;gt;[score]&amp;lt;/score&amp;gt;&lt;br /&gt;
         &amp;lt;/result&amp;gt;&lt;br /&gt;
         &amp;lt;result&amp;gt;&lt;br /&gt;
             &amp;lt;state&amp;gt;[&#039;new&#039; or &#039;regrade&#039;]&amp;lt;/state&amp;gt;&lt;br /&gt;
             &amp;lt;assignment&amp;gt;[idnumber]&amp;lt;/assignment&amp;gt;&lt;br /&gt;
             &amp;lt;student&amp;gt;[studentid]&amp;lt;/student&amp;gt;&lt;br /&gt;
             &amp;lt;score&amp;gt;[score]&amp;lt;/score&amp;gt;&lt;br /&gt;
         &amp;lt;/result&amp;gt;&lt;br /&gt;
         [...]&lt;br /&gt;
 &amp;lt;/results&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Capabilities and Permissions ==&lt;br /&gt;
&lt;br /&gt;
* moodle/grade:view  -  view own grades or grades of other user if used in CONTEXT_USER&lt;br /&gt;
&lt;br /&gt;
* moodle/grade:viewall - view grades of all users&lt;br /&gt;
&lt;br /&gt;
* moodle/grade:viewhidden - see grades that are marked as hidden for the owner&lt;br /&gt;
&lt;br /&gt;
* moodle/grade:hide - be able to hide/unhide cells, items or categories&lt;br /&gt;
&lt;br /&gt;
* moodle/grade:lock - be able to lock cells, items or categories&lt;br /&gt;
&lt;br /&gt;
* moodle/grade:unlock - be able to unlock cells, items or categories&lt;br /&gt;
&lt;br /&gt;
* moodle/grade:manage - manage grade items and categories in gradebook (create, edit, lock, hide, delete, etc.)&lt;br /&gt;
&lt;br /&gt;
* moodle/grade:import - general import grades, requires separate permission for each plugin&lt;br /&gt;
&lt;br /&gt;
* moodle/grade:export - export grades, requires separate permission for each plugin&lt;br /&gt;
&lt;br /&gt;
* gradereport/grader:view - can view the grader report&lt;br /&gt;
&lt;br /&gt;
* gradeimport/csv:view - can view/use the csv import plugin&lt;br /&gt;
&lt;br /&gt;
* gradeexport/csv:view - can view/use the csv export plugin&lt;br /&gt;
&lt;br /&gt;
* moodle:site/accessallgroups&lt;br /&gt;
&lt;br /&gt;
== Development Tasks and Tracking ==&lt;br /&gt;
&lt;br /&gt;
See [http://tracker.moodle.org/browse/MDL-9137 MDL-9137] for the full details.&lt;br /&gt;
&lt;br /&gt;
==Updating module code==&lt;br /&gt;
Module authors must implement new gradebook API and add upgrade code for migration of old grades into new gradebook. Fortunately the needed changes are not big.&lt;br /&gt;
&lt;br /&gt;
Steps:&lt;br /&gt;
*add xxx_update_grades() function into mod/xxx/lib.php&lt;br /&gt;
*add xxx_grade_item_update() function into mod/xxx/lib.php&lt;br /&gt;
*patch xxx_update_instance(), xxx_add_instance() and xxx_delete_instance() to call xxx_grade_item_update()&lt;br /&gt;
*patch all places of code that change grade values to call xxx_update_grades()&lt;br /&gt;
*patch code that displays grades to students to use final grades from the gradebook&lt;br /&gt;
&lt;br /&gt;
There are many examples in official modules, assignment has the most advanced implementation.&lt;br /&gt;
&lt;br /&gt;
== Ideas for the future ==&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
TODO: add new meta issue into tracker&lt;br /&gt;
&lt;br /&gt;
*option to aggregate including/excluding hidden grades - needs db changes&lt;br /&gt;
*option to rollback all changes during import operation if anything fails&lt;br /&gt;
*performance improvements&lt;br /&gt;
*conditional activities&lt;br /&gt;
*course completion criteria&lt;br /&gt;
*better public API for modules&lt;br /&gt;
*better API for gradebook plugins&lt;br /&gt;
*better state tracking in export plugins&lt;br /&gt;
*ajax reports&lt;br /&gt;
*specialised reports&lt;br /&gt;
*submission and marking date tracking db changes&lt;br /&gt;
*calculation formula improvements&lt;br /&gt;
*historical views&lt;br /&gt;
*individual graph of grades (time vs %). Bar graph, lineal graph. Add (or not) the maximum posible; line of 0 (=minimum), 25, 50 (=median), 75 and 100 (=max) percentils of the group.&lt;br /&gt;
*&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
&lt;br /&gt;
* [http://moodle.org/mod/forum/discuss.php?d=69223&amp;amp;mode=3 Gradebook Development ideas] forum discussion&lt;br /&gt;
* Using Moodle [http://moodle.org/mod/forum/discuss.php?d=51107 New gradebook for Moodle] forum discussion&lt;br /&gt;
* [[Development:Gradebook Report Tutorial]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer|Grades]]&lt;br /&gt;
[[Category:Grades]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Template:obsolete_design&amp;diff=45105</id>
		<title>Template:obsolete design</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Template:obsolete_design&amp;diff=45105"/>
		<updated>2008-10-10T14:11:11Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Adding obsolete design article template&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&amp;lt;div class=&amp;quot;notice metadata&amp;quot; id=&amp;quot;stub&amp;quot; style=&amp;quot;clear:both;&amp;quot;&amp;gt;&amp;lt;p class=&amp;quot;note&amp;quot;&amp;gt;&#039;&#039;This article is a design document which is &amp;lt;strong&amp;gt;no longer in use&amp;lt;/strong&amp;gt;, the code it describes having been written and added to the Moodle codebase. The information it contains is likely to be &amp;lt;strong&amp;gt;out of date&amp;lt;/strong&amp;gt;, especially API and Database Schema specifications.&#039;&#039;&amp;lt;/p&amp;gt;&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;includeonly&amp;gt;[[Category:Obsolete_Design]]&amp;lt;/includeonly&amp;gt;&lt;br /&gt;
&amp;lt;noinclude&amp;gt;This template will categorize articles that include it into [[:Category:Obsolete_Design]].&lt;br /&gt;
&amp;lt;/noinclude&amp;gt;&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Category_aggregation&amp;diff=45059</id>
		<title>Category aggregation</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Category_aggregation&amp;diff=45059"/>
		<updated>2008-10-10T13:01:18Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Adding info about extra credits for sum of grades aggregation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Grades}}&lt;br /&gt;
&lt;br /&gt;
== Overview ==&lt;br /&gt;
This menu lets you choose the aggregation strategy that will be used to calculate each participant&#039;s overall grade for a [[Grade categories|grade category]]. The different options are explained below.&lt;br /&gt;
&lt;br /&gt;
The grades are first converted to percentage values (interval from 0 to 1), then aggregated using one of the strategies below and finally converted to the associated category item&#039;s range (between Minimum grade and Maximum grade).&lt;br /&gt;
&lt;br /&gt;
Important: An empty grade is simply a missing gradebook entry, and could mean different things. For example, it could be a participant who hasn&#039;t yet submitted an assignment, an assignment submission not yet graded by the teacher, or a grade that has been manually deleted by the gradebook administrator. Caution in interpreting these &amp;quot;empty grades&amp;quot; is thus advised.&lt;br /&gt;
&lt;br /&gt;
== Aggregation strategies ==&lt;br /&gt;
&lt;br /&gt;
=== Mean of grades ===&lt;br /&gt;
The sum of all grades divided by the total number of grades.&lt;br /&gt;
    A1 70/100, A2 20/80, A3 10/10, category max 100:&lt;br /&gt;
    (0.7 + 0.25 + 1.0)/3 = 0.65 --&amp;gt; 65/100&lt;br /&gt;
=== Weighted mean ===&lt;br /&gt;
Each grade item can be given a weight, which is then used in the arithmetic mean aggregation to influence the importance of each item in the overall mean.&lt;br /&gt;
    A1 70/100 weight 10, A2 20/80 weight 5, A3 10/10 weight 3, category max 100:&lt;br /&gt;
    (0.7*10 + 0.25*5 + 1.0*3)/18 = 0.625 --&amp;gt; 62.5/100&lt;br /&gt;
=== Simple weighted mean ===&lt;br /&gt;
The difference from Weighted mean is that weight is calculated as Maximum grade - Minimum grade for each item. 100 point assignment has weight 100, 10 point assignment has weight 10.&lt;br /&gt;
    A1 70/100, A2 20/80, A3 10/10, category max 100:&lt;br /&gt;
    (0.7*100 + 0.25*80 + 1.0*10)/190 = 0.526 --&amp;gt; 52.6/100&lt;br /&gt;
=== Mean of grades (with extra credits) ===&lt;br /&gt;
Arithmetic mean with a twist. An old, now unsupported aggregation strategy provided here only for backward compatibility with old activities.&lt;br /&gt;
=== Median of grades ===&lt;br /&gt;
The middle grade (or the mean of the two middle grades) when grades are arranged in order of size. The advantage over the mean is that it is not affected by outliers (grades which are uncommonly far from the mean).&lt;br /&gt;
    A1 70/100, A2 20/80, A3 10/10, category max 100:&lt;br /&gt;
    0.7 + 0.25 + 1.0 --&amp;gt; 0.25 --&amp;gt; 25/100&lt;br /&gt;
=== Smallest grade ===&lt;br /&gt;
The result is the smallest grade after normalisation. It is usually used in combination with Aggregate only non-empty grades.&lt;br /&gt;
    A1 70/100, A2 20/80, A3 10/10, category max 100:&lt;br /&gt;
    min(0.7 + 0.25 + 1.0) = 0.25 --&amp;gt; 25/100&lt;br /&gt;
=== Highest grade ===&lt;br /&gt;
The result is the highest grade after normalisation.&lt;br /&gt;
    A1 70/100, A2 20/80, A3 10/10, category max 100:&lt;br /&gt;
    max(0.7 + 0.25 + 1.0) = 1.0 --&amp;gt; 100/100&lt;br /&gt;
=== Mode of grades ===&lt;br /&gt;
The mode is the grade that occurs the most frequently. It is more often used for non-numerical grades. The advantage over the mean is that it is not affected by outliers (grades which are uncommonly far from the mean). However it loses its meaning once there is more than one most frequently occurring grade (only one is kept), or when all the grades are different from each other.&lt;br /&gt;
    A1 70/100, A2 35/50, A3 20/80, A4 10/10, A5 7/10 category max 100:&lt;br /&gt;
    mode(0.7; 0.7; 0.25; 1.0; 0.7) = 0.7 --&amp;gt; 70/100&lt;br /&gt;
=== Sum of grades ===&lt;br /&gt;
The sum of all grade values. Scale grades are ignored. This is the only type that does not convert the grades to percentages internally. The Maximum grade of associated category item is calculated automatically as a sum of maximums from all aggregated items.&lt;br /&gt;
    A1 70/100, A2 20/80, A3 10/10:&lt;br /&gt;
    70 + 20 + 10 = 100/190&lt;br /&gt;
&lt;br /&gt;
When the &amp;quot;Sum of grades&amp;quot; aggregation strategy is used, a grade item can act as Extra credit for the category. This means that the grade item&#039;s maximum grade will not be added to the category total&#039;s maximum grade, but the item&#039;s grade will. Following is an example:&lt;br /&gt;
&lt;br /&gt;
* Item 1 is graded 0-100&lt;br /&gt;
* Item 2 is graded 0-75&lt;br /&gt;
* Item 1 has the &amp;quot;Act as extra credit&amp;quot; checkbox ticked, Item 2 doesn&#039;t.&lt;br /&gt;
* Both items belong to Category 1, which has &amp;quot;Sum of grades&amp;quot; as its aggregation strategy&lt;br /&gt;
* Category 1&#039;s total will be graded 0-75&lt;br /&gt;
* A student gets graded 20 on Item 1 and 70 on Item 2&lt;br /&gt;
* The student&#039;s total for Category 1 will be 75/75 (20+70 = 90 but Item 1 only acts as extra credit, so it brings the total to its maximum)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[ca:Agregació de les categories]]&lt;br /&gt;
[[fr:Tendance centrale de la catégorie]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Quality_assurance&amp;diff=44205</id>
		<title>Quality assurance</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Quality_assurance&amp;diff=44205"/>
		<updated>2008-09-22T07:41:21Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Ideas/Notes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{stub}}[[Category:Quality Assurance]]&lt;br /&gt;
=Introduction=&lt;br /&gt;
==What is QA==&lt;br /&gt;
Here few documents related to QA/Testing on Wikipedia: [http://en.wikipedia.org/wiki/Software_quality_assurance], [http://en.wikipedia.org/wiki/Software_testing]&lt;br /&gt;
&lt;br /&gt;
==QA Objective==&lt;br /&gt;
Main objective: is a major version right for release?&lt;br /&gt;
&lt;br /&gt;
A major version is right when (TBD):&lt;br /&gt;
* blocker bug number &amp;lt; 1%&lt;br /&gt;
* critical bug number &amp;lt; 5%&lt;br /&gt;
* major bug number &amp;lt; 20%&lt;br /&gt;
* test cases have been written/updated for new features&lt;br /&gt;
* all test cases are marked as &#039;&#039;Passed&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
A successful QA cycle assures that we have tested all major Moodle functionalities.&lt;br /&gt;
&lt;br /&gt;
==Participate to QA for your benefit==&lt;br /&gt;
The document described the Moodle QA. Any participation is welcome. Please use the tab &amp;quot;page comments&amp;quot; in order to participate. A good Moodle QA will be a great benefit for Moodle project but also for any entity using Moodle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
= QA cycle =&lt;br /&gt;
We use Jira as Test Case Management System, see the specific chapter at the bottom of this page.&lt;br /&gt;
&lt;br /&gt;
== Setting a QA cycle ==&lt;br /&gt;
We will need to write into Jira the test cases the first time, then update them for any new QA cycle. In fact it would be good that people writing/updating the functional specs, update the test cases in the same time. It would not take so long for them. Another possibility: create a test case status &amp;quot;&#039;&#039;Cannot be run&#039;&#039;&amp;quot;. So when a tester cannot run a test case because the feature changed, the QA manager is aware that this specific test case needs to be updated.&lt;br /&gt;
&lt;br /&gt;
== Running a QA cycle ==&lt;br /&gt;
QA participants will choose into Jira the feature they want to test (status set to &amp;quot;&#039;&#039;Not run&#039;&#039;&amp;quot; or &amp;quot;&#039;&#039;to retest&#039;&#039;&amp;quot;). &lt;br /&gt;
* If the tester finds a blocker/critical/major bug, he sets the test case to &amp;quot;&#039;&#039;Failed&#039;&#039;&amp;quot;. Then he writes a bug issue and links the test case to the bug issue. &amp;quot;&#039;&#039;Failed&#039;&#039;&amp;quot; test cases will be set to &amp;quot;&#039;&#039;to retest&#039;&#039;&amp;quot; by the bug fixer. &lt;br /&gt;
* If the bug is minor and the feature is working, the tester still writes a bug issue and still links the test case to the bug issue. However he sets the test case to &amp;quot;&#039;&#039;Passed&#039;&#039;&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
== Validate a QA cycle ==&lt;br /&gt;
The Test Case management system (Jira) needs to display:&lt;br /&gt;
* how many test cases are &amp;quot;&#039;&#039;failed&#039;&#039;&amp;quot;,&amp;quot;&#039;&#039;passed&#039;&#039;&amp;quot;,&amp;quot;&#039;&#039;not run&#039;&#039;&amp;quot;,&amp;quot;&#039;&#039;to retest&#039;&#039;&amp;quot; for a specific version/component. (test case issues) &lt;br /&gt;
* how many critical/major bugs for a specific version/component (bug issues)&lt;br /&gt;
&amp;lt;br&amp;gt;From these results the QA manager valid the version for releasing.&lt;br /&gt;
&lt;br /&gt;
=Draft/Ideas/Opinion/Requirements/Notes=&lt;br /&gt;
==Need to be identified==&lt;br /&gt;
QA Cost (in persons-hours) for:&lt;br /&gt;
* writting test cases (the first time)&lt;br /&gt;
* preparing a QA cycle (update test cases, get a new QA system ready for testing)&lt;br /&gt;
* running all test cases&lt;br /&gt;
&lt;br /&gt;
When should a QA cycle be run?&amp;lt;br&amp;gt;&lt;br /&gt;
What function specs/Use Cases have we got?&amp;lt;br&amp;gt;&lt;br /&gt;
Who can write test cases?&amp;lt;br&amp;gt;&lt;br /&gt;
Who can run test cases?&amp;lt;br&amp;gt;&lt;br /&gt;
Do we need test data?&lt;br /&gt;
&lt;br /&gt;
==Ideas/Notes==&lt;br /&gt;
* We would need to make a clear statement in the Moodle docs in order to make a difference between: User Manuals, Function Specifications, Technical Specifications, Requirements, ... and many existing mixed document. (The moodle docs are a powerful source of information, however at the current time Google is your best friend for finding information.)&lt;br /&gt;
* There is a lack of detailed functional specifications and Use Cases =&amp;gt; at this moment only developers and experimented users would be able to write test cases with missing use cases.&lt;br /&gt;
* I need to study Quality Assurance into open source world: how do the other projects manage their QA department?&lt;br /&gt;
* Moodle doesn&#039;t have deadline. It is released when it will be ready. However take care about no-end QA phase.&lt;br /&gt;
* there are many experimented users who can test Moodle.&lt;br /&gt;
* some people/companies using Moodle probably have already written test plans, test cases, maybe even use cases.&lt;br /&gt;
* need documentation on how to write a test case and how to run test cases. Need also document for QA manager (setting a QA cycle, read the result, managing the QA cycle)&lt;br /&gt;
* need to link Moodledocs QA documents with Moodle docs tracker documents, organise an easy-to-read Quality Engineering section in Moodledocs (include Code Testing as well).&lt;br /&gt;
* we could create a testing program as Netbeans [http://qa.netbeans.org/processes/cat/65/index.html]&lt;br /&gt;
* create a Moodle package including test data for people testing in local&lt;br /&gt;
* I don&#039;t think we need smoke tests as we don&#039;t really build anything in PHP.&lt;br /&gt;
&lt;br /&gt;
==Jira as Test Case Management System==&lt;br /&gt;
The main reason to use Jira as Test Case Management System is that the Moodle community is familiar with. It will also be easy to link test case issues to bug issues.&lt;br /&gt;
* The search tool of Jira will provide the QA results.&lt;br /&gt;
* we will create a main issue containing thousand of sub-tasks. These subtasks will be the test cases. Then for every new QA cycle we will clone this main issue. Need to identify how to change subtask version quickly/easily.&lt;br /&gt;
* re-write the &#039;&#039;Jira as a Test Case Management Software documentation&#039;&#039; [https://docs.moodle.org/en/Jira_as_a_Test_Case_Management_Software]&lt;br /&gt;
&lt;br /&gt;
==Other QA from popular open-source projects==&lt;br /&gt;
* Netbeans wrote specification tests [http://wiki.netbeans.org/TestSpecifications]. They have a QA program [http://qa.netbeans.org/processes/cat/65/index.html]&lt;br /&gt;
* Firefox use an integrated testcase management and QA tool called Litmus [https://litmus.mozilla.org/]. An example of a test case: [https://litmus.mozilla.org/show_test.cgi?id=5036]. They&#039;ve got more than 7000 test cases.&lt;br /&gt;
* OpenOffice has a QA page [http://qa.openoffice.org/ooQAReloaded/ooQA-ManualTesting.html]. I found it a bit a hassle to navigate into these pages. Make some succinct documentation for Moodle QA tester in order to start quickly and easily.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Admin_settings&amp;diff=43974</id>
		<title>Development:Admin settings</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Admin_settings&amp;diff=43974"/>
		<updated>2008-09-18T12:04:50Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Fixing typos&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Moodle&#039;s configuration is stored in a mixture of the config, config_plugins, and a few other tables. These settings are edited through the administration screens, which can be accessed by going to the .../admin/index.php URL on your moodle site, or using the Administration block that appears to administrators of the Moodle front page. This page explains how the code for displaying and editing of these settings works.&lt;br /&gt;
&lt;br /&gt;
==Where to find the code==&lt;br /&gt;
&lt;br /&gt;
This is explained further below, but in summary:&lt;br /&gt;
* The library code is all in lib/adminlib.php.&lt;br /&gt;
* The definition of the all the parts of the admin tree is in admin/settings/* some of which call out to plugins to see if they have settings they want to add.&lt;br /&gt;
* The editing and saving of settings is managed by admin/settings.php, admin/upgradesettings.php, and admin/search.php.&lt;br /&gt;
* The administration blocks that appear on the front page and on most of the admin screens is in blocks/admin_tree and blocks/admin_bookmarks.&lt;br /&gt;
&lt;br /&gt;
In my experience most of this code is pretty easy to understand and work with, so here I will focus on giving an overview. For details, see the code.&lt;br /&gt;
&lt;br /&gt;
==The building blocks==&lt;br /&gt;
&lt;br /&gt;
All the settings are arranged into a tree structure. This tree structure is represented in memory as a tree of PHP objects.&lt;br /&gt;
&lt;br /&gt;
At the root of the tree is an &#039;&#039;&#039;admin_root&#039;&#039;&#039; object.&lt;br /&gt;
&lt;br /&gt;
That has children that are &#039;&#039;&#039;admin_category&#039;&#039;&#039;s.&lt;br /&gt;
&lt;br /&gt;
Admin categories contain other categories, &#039;&#039;&#039;admin_settingpage&#039;&#039;&#039;s, and &#039;&#039;&#039;admin_externalpage&#039;&#039;&#039;s.&lt;br /&gt;
&lt;br /&gt;
Settings pages contain individual &#039;&#039;&#039;admin_setting&#039;&#039;&#039;s.&lt;br /&gt;
&lt;br /&gt;
admin_setting is a base class with lots of subclasses like &#039;&#039;&#039;admin_setting_configtext&#039;&#039;&#039;, &#039;&#039;&#039;admin_setting_configcheckbox&#039;&#039;&#039;, and so on. If you need to, you can create new subclasses.&lt;br /&gt;
&lt;br /&gt;
External pages are for things that do not fit into the normal settings structure. For example the global assign roles page, or the page for managing activity modules.&lt;br /&gt;
&lt;br /&gt;
==How the tree is built==&lt;br /&gt;
&lt;br /&gt;
When Moodle needs the admin tree, it calls admin_get_root in lib/adminlib.php, which&lt;br /&gt;
# creates a global $ADMIN object which is an instance of admin_root.&lt;br /&gt;
# does require_once admin/settings/top.php, which adds the top level categories to $ADMIN.&lt;br /&gt;
# does require_once on all the other files in admin/settings to add more specific settings pages and the settings themselves. Some of these settings files additionally make calls out to various types of plugins. For example&lt;br /&gt;
#* admin/settings/plugins.php gives activity modules, blocks, question types, ... a chance to add admin settings.&lt;br /&gt;
# adds the admin reports to the tree.&lt;br /&gt;
&lt;br /&gt;
As an optimisation, before building each bit of the tree, some capability checks are performed, and bits of the tree are skipped if the current user does not have permission to access them.&lt;br /&gt;
&lt;br /&gt;
Exactly how different types of plugin should add their settings to the tree should be documented in the [[Development:Developer_documentation#Make_a_new_plugin|instructions for writing that sort of plugin]].&lt;br /&gt;
&lt;br /&gt;
==Individual settings==&lt;br /&gt;
&lt;br /&gt;
Let us look at a simple example: [http://cvs.moodle.org/moodle/mod/forum/settings.php?view=markup mod/forum/settings.php]. This is included by admin/settings/plugins.php, which has already created $settings, which is an admin_settingpage that we can add to. The file contains lots of lines that look a bit like:&lt;br /&gt;
&lt;br /&gt;
 $settings-&amp;gt;add(new admin_setting_configcheckbox(&#039;forum_replytouser&#039;, get_string(&#039;replytouser&#039;, &#039;forum&#039;),&lt;br /&gt;
                    get_string(&#039;configreplytouser&#039;, &#039;forum&#039;), 1));&lt;br /&gt;
&lt;br /&gt;
What this means is that to our settings page, we are adding a checkbox setting. To understand this in more detail, we need to know what arguments the constructor for an admin_setting takes. The definition of the constructor is:&lt;br /&gt;
&lt;br /&gt;
 function admin_setting($name, $visiblename, $description, $defaultsetting) {&lt;br /&gt;
&lt;br /&gt;
So, $name here is &#039;forum_replytouser&#039;. This means that this setting is stored in the database in the row of the config table where name=&#039;forum_replytouser&#039;, and is accessible as $CFG-&amp;gt;forum_replytouser.&lt;br /&gt;
&lt;br /&gt;
$visiblename is get_string(&#039;replytouser&#039;, &#039;forum&#039;), this is the label that is put in front of setting on the admin screen.&lt;br /&gt;
&lt;br /&gt;
$description is get_string(&#039;configreplytouser&#039;, &#039;forum&#039;), this is a short bit of text displayed underneath the setting to explain it further.&lt;br /&gt;
&lt;br /&gt;
$defaultsetting is the default value for this setting. This value is used when Moodle is installed. For simple settings like checkboxes and text fields, this is a simple value. For some more complicated settings, this is an array.&lt;br /&gt;
&lt;br /&gt;
Let as now look at a more complicated example, from mod/quiz/settingstree.php:&lt;br /&gt;
&lt;br /&gt;
 $quizsettings-&amp;gt;add(new admin_setting_text_with_advanced(&#039;quiz/timelimit&#039;,&lt;br /&gt;
         get_string(&#039;timelimit&#039;, &#039;quiz&#039;), get_string(&#039;configtimelimit&#039;, &#039;quiz&#039;),&lt;br /&gt;
         array(&#039;value&#039; =&amp;gt; &#039;0&#039;, &#039;fix&#039; =&amp;gt; false), PARAM_INT));&lt;br /&gt;
&lt;br /&gt;
This shows two new things:&lt;br /&gt;
&lt;br /&gt;
$name here is &#039;quiz/timelimit&#039;. This is more complicated than a simple setting name. It means that this setting is stored in the config_plugins table, in the row where plugin=&#039;quiz&#039; and name=&#039;timelimit&#039;. It is not accessible through $CFG, to get it you can use get_config(&#039;quiz&#039;, &#039;timelimit&#039;).&lt;br /&gt;
&lt;br /&gt;
And this example shows a $defaultsetting that is an array.&lt;br /&gt;
&lt;br /&gt;
Normally, if you want a particular sort of setting, the easiest way is to look around the admin screens of your Moodle site, and find a setting like the one you want. Then go and copy the code and edit it. Therefore, we do not include a complete list of setting types here.&lt;br /&gt;
&lt;br /&gt;
==External pages==&lt;br /&gt;
&lt;br /&gt;
admin_externalpages represent screens of settings that do not fall into the standard pattern of admin_settings. The admin_externalpage object in the settings tree holds the URL of a PHP page that controls various settings.&lt;br /&gt;
&lt;br /&gt;
In that PHP page, near the start you need to call the function admin_externalpage_setup($pagename), then, instead of the usual print_header and print_footer functions, you use admin_externalpage_print_header() and admin_externalpage_print_footer() functions. This ensures that your page appears with the administration blocks and appropriate navigation.&lt;br /&gt;
&lt;br /&gt;
Note that there are some subclasses of admin_externalpage, for example admin_page_managemods. In a lot of cases, these subclasses only exist to override the search method so this page can be found by appropriate searches.&lt;br /&gt;
&lt;br /&gt;
Once again, to understand this in more depth, your best approach is to look at how some of the external pages in Moodle work.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Development:Modules|adding settings for actvitiy modules]]&lt;br /&gt;
* [[Development:Admin_reports#How_your_report_gets_included_in_the_admin_tree|adding admin reports to the tree]]&lt;br /&gt;
* [[Development:Repository_plugins#APIs_for_Administration|configuration of repository plugins]]&lt;br /&gt;
* [[Development:Filters#Adding_a_settings_screen|configuration for filter]]&lt;br /&gt;
* [[Development:Developer_documentation|Other developer documentation]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Portfolio_API&amp;diff=43719</id>
		<title>Development:Portfolio API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Portfolio_API&amp;diff=43719"/>
		<updated>2008-09-16T18:00:35Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* mod/assignmnet */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}This page describes the specification for a future feature, currently being worked on for [[Roadmap|Moodle 2.0]].  This spec is STILL UNDER CONSTRUCTION.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The Portfolio API is a core set of interfaces that all Moodle code will/should use so that we can easily publish files to all kinds of external document repository systems.&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to remember that portfolios are generally treated as WRITE-ONLY.  All we are doing in Moodle is grabbing stuff and pushing it out to somewhere.  Management of the files and further combining/reflecting is done through the native interface provided by the portfolio system.  Reading of files from a repository is handled by the [[Development:Repository API|Repository API]].&lt;br /&gt;
&lt;br /&gt;
A typical user story:&lt;br /&gt;
&lt;br /&gt;
# When portfolios are enabled, every page or major piece of content in Moodle has a little &amp;quot;Save&amp;quot; button beside it.&lt;br /&gt;
# User clicks one of these buttons&lt;br /&gt;
# User is able to choose from a list of configured portfolios (this step will be skipped if there&#039;s only one).&lt;br /&gt;
# User may be asked to define the format of the captured content (eg pdf, IMS LD, HTML, XML ...)&lt;br /&gt;
# User may be asked to define some metadata to go with the captured content (some will be generated automatically).&lt;br /&gt;
# The content and metadata is COPIED to the external portfolio system&lt;br /&gt;
# User has an option to &amp;quot;Return to the page you left&amp;quot; or &amp;quot;Visit their portfolio&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note this will be just as useful for teachers as for students.&lt;br /&gt;
&lt;br /&gt;
The formatting possibilities will vary depending on the context of the button and the type of external portfolios.  So for example, the &amp;quot;Save&amp;quot; button on the course page would allow the user to capture the whole course in IMS LD or Moodle backup format, which you would not have on a forum page.&lt;br /&gt;
&lt;br /&gt;
==Architecture==&lt;br /&gt;
&lt;br /&gt;
Here is how it will work:&lt;br /&gt;
&lt;br /&gt;
===Plugins and libraries===&lt;br /&gt;
&lt;br /&gt;
There will be one type of plugins &lt;br /&gt;
&lt;br /&gt;
# Portfolio (eg Mahara/Elgg/OSP/Facebook/Download) - this will be portfolio/type/xxx&lt;br /&gt;
&lt;br /&gt;
The transport layer (eg mnet/http/scp/cp/dav etc) or clients (eg box.net/flickr) will be written as libraries, to be shared by both repository and portfolio.&lt;br /&gt;
&lt;br /&gt;
Then there will be different formats that plugins will support (and the part of moodle exporting content must support as well), eg IMS, moodle native, mahara native, pdf, encrypted pdf. These will have good libraries supporting them.&lt;br /&gt;
&lt;br /&gt;
===Admin===&lt;br /&gt;
&lt;br /&gt;
It is important to be allowed to have multiple instances of (some) plugins.  The workflow for adding a new one is:&lt;br /&gt;
&lt;br /&gt;
*Admin navigates to portfolio config&lt;br /&gt;
*Selects from the list of available portfolio plugins, and clicks &#039;add a new external portfolio&#039; (some may be disabled if there is an instance already and the plugin doesn&#039;t support multiple instances)&lt;br /&gt;
*Configure the plugin - select which transport and content types to use if there are multiple supported and installed, urls, authentication keys, etc.&lt;br /&gt;
*Set permissions (maybe) - handled by roles.&lt;br /&gt;
&lt;br /&gt;
This is not necessary for every type of portfolio, because many will just require the user to authenticate directly and if we do ever want to retain settings for each user we just use user preferences.&lt;br /&gt;
&lt;br /&gt;
===Exporting===&lt;br /&gt;
&lt;br /&gt;
*User is viewing a page that calls new portfolio_add_button().  This checks to see if there are any configured portfolio plugin instances, and also (maybe) any permissions related to portfolios, and what the user&#039;s portfolio settings are, and then displays either a single &#039;add to portfolio&#039; button, or a drop down menu of the available systems with the add button.&lt;br /&gt;
*When this button is pressed, the user is redirected to portfolio/add.php, with some post data containing the responsible area (activity module or something like course or blog) callback file and callback arguments, as well as optionally some information about what type of content it is.&lt;br /&gt;
*On this page, the user is presented with a form to enter metadata about the item, and configure any options. At this point if there are multiple formats available for export (based on the intersection of what the plugin and module support), the user can select which format they want.  The plugin and module can both export mform elements for this page.  The user can at this point also select to send the data and wait (with a warning it might take awhile), or queue it for processing if it&#039;s larger.  This is determined by the size of the content to be exported.&lt;br /&gt;
*When the user has submitted the form, they are displayed a summary of what they&#039;re about to export, with &#039;confirm&#039; and &#039;cancel&#039; buttons.  Cancel cancels the request, and cleans up any temporary data, and returns the user to where they came from, while confirm goes to the next step.&lt;br /&gt;
*At any point, the portfolio plugin might need to take control for a step. For example, facebook or flickr might require the user to log in for the first time and confirm moodle is allowed to access their API.&lt;br /&gt;
*When the user has confirmed their summary, a &#039;portfolio_send&#039; event will be triggered.  At this point, one of two things happen.  &lt;br /&gt;
# If the user has elected to wait, the &#039;instant&#039; event is fired, and when the caller gets control again, it displays the status to the user.&lt;br /&gt;
# If the user has elected to queue, the delayed event is fired and the user is notified.&lt;br /&gt;
*The user is given the option to continue to their portfolio, or return to where they were&lt;br /&gt;
*When the event is handled (either through the cron or instant event), the following happens:&lt;br /&gt;
*The event handler is invoked. This reawakens the transfer and defers control to the caller and then the portfolio to prepare and send the package.&lt;br /&gt;
*When this is complete, we return control to the event handler (which, if it&#039;s an &#039;instant&#039; one, will return true to the caller.&lt;br /&gt;
&lt;br /&gt;
===Storage===&lt;br /&gt;
&lt;br /&gt;
Obviously during this process, state is going to be lost between webserver requests and also between user input and event handling. All of the data is stored in the database, in the form of a serialized (and base64 encoded) representation of the exporter, plugin and caller objects.&lt;br /&gt;
&lt;br /&gt;
Files are also going to be written during the preparation stage of the export, and these are stored in a special portfolio area using the new files api.&lt;br /&gt;
&lt;br /&gt;
===Access/Permissions===&lt;br /&gt;
* The calling code is responsible for performing the permission checks necessary before asking to display any button, but during the export the portfolio code will call a check_permissions function on the caller object.&lt;br /&gt;
* I would really like to be able to make some portfolio instances available to some roles but this has fallen out of scope.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Event API===&lt;br /&gt;
&lt;br /&gt;
The portfolio code uses the event api to handle queued events and there is one entry point for this that reawakens the transfer objects and resumes the transfer.  Additionally, portfolio plugins can subscribe to events like any other part of moodle.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Technical==&lt;br /&gt;
&lt;br /&gt;
===Abstract Portfolio Baseclass: portfolio_plugin_base===&lt;br /&gt;
&lt;br /&gt;
Mixes providing some basic functionality by means of its own functions, with a number of abstract functions plugins must implement, and with some functions that plugins can also optionally override.&lt;br /&gt;
&lt;br /&gt;
See also: [[Development:Writing_a_Portfolio_Plugin]] for a full list of all methods you must/can/shouldn&#039;t override, as well as associated instructions for what else you need to do to create a new portfolio plugin&lt;br /&gt;
&lt;br /&gt;
===Abstract Caller Baseclass : portfolio_caller_base===&lt;br /&gt;
&lt;br /&gt;
Whenever somewhere in Moodle wants an &#039;add to portfolio&#039; button, they must subclass this.&lt;br /&gt;
&lt;br /&gt;
See also: [[Development:Adding_a_Portfolio_Button_to_a_page]] for a full list of all methods you must/can/shouldn&#039;t override as well as the associated instructions for how to call portfolio_add_button.&lt;br /&gt;
&lt;br /&gt;
===Database Tables===&lt;br /&gt;
&lt;br /&gt;
The actual information about plugins that are installed is just stored in mdl_config_plugin.&lt;br /&gt;
&lt;br /&gt;
Additionally, as we&#039;re configuring instances of plugins, rather than just one config set per plugin, we&#039;re not using mdl_config_plugin, but instead our own set of tables:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;portfolio_instance:&#039;&#039;&#039;&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;
|-&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Datatype&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|integer&lt;br /&gt;
|sequence&lt;br /&gt;
|-&lt;br /&gt;
|plugin&lt;br /&gt;
|varchar(50)&lt;br /&gt;
|name of plugin (should match directory in portfolio/type)&lt;br /&gt;
|-&lt;br /&gt;
|name&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of this plugin instance&lt;br /&gt;
|-&lt;br /&gt;
|visible&lt;br /&gt;
|smallint&lt;br /&gt;
|0 or 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;portfolio_instance_config:&#039;&#039;&#039;&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;
|-&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Datatype&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|integer&lt;br /&gt;
|sequence&lt;br /&gt;
|-&lt;br /&gt;
|instance&lt;br /&gt;
|integer&lt;br /&gt;
|(pseudo)fk to portfolio_instance&lt;br /&gt;
|-&lt;br /&gt;
It cannot, however, be responsible for how external systems deal with this case. The different plugins can do what they can. For example, mahara will create new files rather than overwrite. The box.net plugin will try very hard to rename files to avoid collisions. &lt;br /&gt;
|name&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|config name&lt;br /&gt;
|-&lt;br /&gt;
|value&lt;br /&gt;
|text&lt;br /&gt;
|config value&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;portfolio_instance_user:&#039;&#039;&#039;&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;
|-&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Datatype&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|integer&lt;br /&gt;
|sequence&lt;br /&gt;
|-&lt;br /&gt;
|instance&lt;br /&gt;
|integer&lt;br /&gt;
|(pseudo)fk to portfolio_instance&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|integer&lt;br /&gt;
|(pseudo)fk to mdl_user&lt;br /&gt;
|-&lt;br /&gt;
|name&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|config name&lt;br /&gt;
|-&lt;br /&gt;
|value&lt;br /&gt;
|text&lt;br /&gt;
|config value&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;portfolio_log:&#039;&#039;&#039;&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;
|-&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Datatype&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|integer&lt;br /&gt;
|sequence&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|integer&lt;br /&gt;
|(pseudo) fk to mdl_user&lt;br /&gt;
|-&lt;br /&gt;
|time&lt;br /&gt;
|integer&lt;br /&gt;
|unix timestamp of transfer&lt;br /&gt;
|-&lt;br /&gt;
|portfolio&lt;br /&gt;
|integer&lt;br /&gt;
|(pseudo) fk to mdl_portfolio_instance&lt;br /&gt;
|-&lt;br /&gt;
|caller_class&lt;br /&gt;
|varchar(150)&lt;br /&gt;
|name of caller class (used in the case of duplicates to display information)&lt;br /&gt;
|-&lt;br /&gt;
|caller_file&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|file that contains the definition of caller_class&lt;br /&gt;
|-&lt;br /&gt;
|caller_sha1&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|sha1 information of export&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;portfolio_tempdata&#039;&#039;&#039;&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;
|-&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Datatype&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|integer&lt;br /&gt;
|sequence&lt;br /&gt;
|-&lt;br /&gt;
|data&lt;br /&gt;
|text&lt;br /&gt;
|serialized representation of export data&lt;br /&gt;
|-&lt;br /&gt;
|expirytime&lt;br /&gt;
|integer&lt;br /&gt;
|time this data (and the transfer) expires (and the record (and associated files)) will be deleted&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|integer&lt;br /&gt;
|psuedo fk to mdl_user&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All plugins can also implement their own database tables as needed, by creating a db/install.xml and db/upgrade.php inside portfolio/type/xxx/ (See [[Development:Writing_a_Portfolio_Plugin]]) for more information.&lt;br /&gt;
&lt;br /&gt;
===Portfolio Plugins===&lt;br /&gt;
&lt;br /&gt;
#[[Development:Mahara_Portfolio_Plugin|mahara]] (will be done for the initial implementation)&lt;br /&gt;
#download (will be done for the initial implementation) &lt;br /&gt;
#box.net (will be done for the initial implementation)&lt;br /&gt;
#flickr (Nico has been writing this but it is incomplete)&lt;br /&gt;
#googledocs (I think DanP has been writing this)&lt;br /&gt;
&lt;br /&gt;
transport types and formats should be able to be found in a shared location for multiple plugins of both portfolio and repository type to use, but also might be specific to one type of plugin which means that moodle should support looking in multiple locations for these plugins. (eg mahara native format would be in the mahara portfolio plugin, but pdf format will be in a shared library)&lt;br /&gt;
&lt;br /&gt;
===Possible Transport Types===&lt;br /&gt;
&lt;br /&gt;
# mnet (will be done for the initial implementation as part of the Mahara Portfolio Plugin)&lt;br /&gt;
# download (just uses send_file_* functions)&lt;br /&gt;
# http&lt;br /&gt;
# filesystem (could be local/nfs/samba whatever) (cp)&lt;br /&gt;
# ssh based (eg scp - should find and re-use the elgg block code as it deals with using ssh keys nicely)&lt;br /&gt;
# webdav&lt;br /&gt;
# open social? (http://code.google.com/apis/opensocial/)&lt;br /&gt;
&lt;br /&gt;
===Possible Export Formats===&lt;br /&gt;
&lt;br /&gt;
(Note that we don&#039;t necessarily want more than one of these for the initial implementation)&lt;br /&gt;
&lt;br /&gt;
* implemented now:&lt;br /&gt;
# html &lt;br /&gt;
# image&lt;br /&gt;
# video&lt;br /&gt;
# plaintext&lt;br /&gt;
# &#039;file&#039; (fallback)&lt;br /&gt;
&lt;br /&gt;
* possibly implemented in the future&lt;br /&gt;
# pdf&lt;br /&gt;
# encrypted pdf&lt;br /&gt;
# ims?&lt;br /&gt;
# leap/piop? (http://wiki.cetis.ac.uk/LEAP_2.0) &lt;br /&gt;
# moodle native?&lt;br /&gt;
# mahara native?&lt;br /&gt;
# Dublin Core (Enovation implemented this)&lt;br /&gt;
&lt;br /&gt;
 &lt;br /&gt;
===Testing===&lt;br /&gt;
&lt;br /&gt;
At this stage, the portfoliolib and button objects have tests, and the callers have tests to check whether their sha1 generation remains consistent appropriately.  This includes the implicit testing of constructing the caller objects (which verifies the callback arguments).&lt;br /&gt;
&lt;br /&gt;
The plugins are not currently tested and even if they were we would not be able to test interaction with the remote system.&lt;br /&gt;
&lt;br /&gt;
==MNET==&lt;br /&gt;
&lt;br /&gt;
This section has moved to [[Development:MNET_Roadmap]]&lt;br /&gt;
&lt;br /&gt;
See also [[Development:MNET_API]] for the documentation of xmlrpc functions&lt;br /&gt;
&lt;br /&gt;
==Duplication of Data==&lt;br /&gt;
&lt;br /&gt;
Moodle will keep track of what content it transfers and when.  It keeps a sha1 has of the data, so that if the user tries to export the same content, Moodle can warn the user.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
It cannot, however, be responsible for how external systems deal with this case. The different plugins can do what they can.  For example, mahara will create new files rather than overwrite.  The box.net plugin will try very hard to rename files to avoid collisions.&lt;br /&gt;
&lt;br /&gt;
==Save points in Moodle==&lt;br /&gt;
&lt;br /&gt;
moved to http://tracker.moodle.org/browse/MDL-15758 during development&lt;br /&gt;
&lt;br /&gt;
==Still TODO==&lt;br /&gt;
&lt;br /&gt;
There are a few things still I have not been able to complete for various reasons (generally reliance on other parts of the system, eg Files API).  There are bugs for all of these, but reproduced here for completeness:&lt;br /&gt;
&lt;br /&gt;
* MDL-16406 - waiting on QA (Jerome)&lt;br /&gt;
* MDL-16048 - waiting on QA (Nico)&lt;br /&gt;
* MDL-16313 - this just didn&#039;t get far enough up my list and I&#039;m still not sure how relevant it is.&lt;br /&gt;
* MDL-15777 - reliance on Files API - data fields that subclass data_field_file need to be extracted and copied separately into the export area using copy_existing_file - this is essentially done but I still think picture should subclass file. More info in MDL-16493&lt;br /&gt;
* MDL-15777 - reliance on Files API - data module can only export as CSV even though plain export also supports ods/xls as those two libraries are not updated to the new Files API.  More info in MDL-15911&lt;br /&gt;
* MDL-16326 - reliance on Files API - &#039;file&#039; resource module has not been updated to use Files API, so exporting from this type is not yet implemented (currently HTML and plaintext only)&lt;br /&gt;
* MDL-16175 - (currently) unreasonable reliance on exceptions.  Especially for queued events, currently if a portfolio transfer is woken up at cron to be completed and an error happens in mnet (eg one remote site is down or misconfigured), the entire cronjob will die as mnet functions call print_error, which calls die(). This essentially means cron will stay broken (for all of moodle) until that transfer expires.   This is not really a bug in portfolio code, but it definitely exacerbates an already brittle situation.&lt;br /&gt;
&lt;br /&gt;
===Unit Test TODO===&lt;br /&gt;
&lt;br /&gt;
Currently there&#039;s quite a few tests implemented, but outstanding are tests that rely on the generator to create files for the callers.  As the generator gets updated to create this data, the portfolio unit tests will start throwing exceptions in the portfolio_exporter_text-&amp;gt;copy_existing_file method so that it will become obvious when this needs to be updated as the tests will start failing (they are currently passing)&lt;br /&gt;
&lt;br /&gt;
==Current exhaustive list of export scenarios==&lt;br /&gt;
&lt;br /&gt;
===mod/assignment===&lt;br /&gt;
&lt;br /&gt;
====upload single file====&lt;br /&gt;
&lt;br /&gt;
This is pretty straightforward.  It should display the export icon next to the single file (no large form/button), and the export should respect the mime-based subtypes (_IMAGE, _VIDEO etc)&lt;br /&gt;
&lt;br /&gt;
====upload multiple files====&lt;br /&gt;
&lt;br /&gt;
This one is a little more complex. You should get an export icon next to individual files, and, additionally, if there is more than one, an export form at the bottom (which will export all files).   Exporting multiple files will always stop mime detection and fallback to _FILE format.&lt;br /&gt;
&lt;br /&gt;
====online text====&lt;br /&gt;
&lt;br /&gt;
Displays the full form at the bottom of the page. Should export as format _HTML.&lt;br /&gt;
&lt;br /&gt;
===mod/chat===&lt;br /&gt;
&lt;br /&gt;
These should all export as format _HTML, and contain no references back to Moodle (eg user profile images, which won&#039;t be able to be seen necessarily)&lt;br /&gt;
&lt;br /&gt;
====Session page====&lt;br /&gt;
&lt;br /&gt;
Should export entire session.&lt;br /&gt;
&lt;br /&gt;
====Report page per session====&lt;br /&gt;
&lt;br /&gt;
Should export entire session.&lt;br /&gt;
&lt;br /&gt;
====Report page all sessions====&lt;br /&gt;
&lt;br /&gt;
Should concatenate all sessions together.&lt;br /&gt;
&lt;br /&gt;
===mod/data===&lt;br /&gt;
&lt;br /&gt;
====Single entry export====&lt;br /&gt;
&lt;br /&gt;
Should export as HTML.  Any files should be included along with the html.  If there is only one field in the entry and it is a file or image, the mimetype should be respected, and the export format should be based on that (eg _IMAGE)&lt;br /&gt;
&lt;br /&gt;
====Whole database instance export====&lt;br /&gt;
&lt;br /&gt;
Should export as CSV.   Files are not included (This is the same as the other CSV export)&lt;br /&gt;
&lt;br /&gt;
===mod/forum===&lt;br /&gt;
&lt;br /&gt;
====Whole discussion====&lt;br /&gt;
&lt;br /&gt;
The export format is FILE, attachments come alongside discussion.html - this could be improved later to be HTML if there are no attachments.&lt;br /&gt;
&lt;br /&gt;
====Single post====&lt;br /&gt;
&lt;br /&gt;
The export format is _HTML.&lt;br /&gt;
&lt;br /&gt;
====Single post with attachments====&lt;br /&gt;
&lt;br /&gt;
The export format is FILE as it&#039;s mixed, and attachments come alongside post.html.&lt;br /&gt;
&lt;br /&gt;
====Single attachment====&lt;br /&gt;
&lt;br /&gt;
Mimetypes should be respected and the export format should be based on them (eg _IMAGE)&lt;br /&gt;
&lt;br /&gt;
===mod/glossary===&lt;br /&gt;
&lt;br /&gt;
====Single entry export====&lt;br /&gt;
&lt;br /&gt;
Should export the entry as HTML.&lt;br /&gt;
&lt;br /&gt;
====Whole glossary export====&lt;br /&gt;
&lt;br /&gt;
Should export the glossary as CSV - similar to a current glossary export.&lt;br /&gt;
&lt;br /&gt;
===mod/resource===&lt;br /&gt;
&lt;br /&gt;
====HTML resource====&lt;br /&gt;
&lt;br /&gt;
Should export as _HTML.&lt;br /&gt;
&lt;br /&gt;
====text resource====&lt;br /&gt;
&lt;br /&gt;
Should export as _TEXT.&lt;br /&gt;
&lt;br /&gt;
====file resource====&lt;br /&gt;
&lt;br /&gt;
Not implemented yet. Blocked by FILES API. Should respect the mimetype of the file and export in the appropriate format.&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
* [[Development:Repository API]]&lt;br /&gt;
* [[Development:File API]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Talk:Gradebook_1.9_Tutorial&amp;diff=43254</id>
		<title>Talk:Gradebook 1.9 Tutorial</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Talk:Gradebook_1.9_Tutorial&amp;diff=43254"/>
		<updated>2008-09-08T12:57:23Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* No Item Weight setting for categories */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Strategy 1: Items weighted by their point values&lt;br /&gt;
&lt;br /&gt;
I would call this Strategy &amp;quot;Unweighted&amp;quot; since the weight of each item is 1.  I guess this is what Moodle means by &amp;quot;simple weighted&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== No Item Weight setting for categories ==&lt;br /&gt;
&lt;br /&gt;
In my Moodle 1.9.2 I can see no &#039;&#039;Item Weight&#039;&#039; setting for grade categories (it&#039;s mentioned in the tutorial). But there is an item called &#039;&#039;Aggregation coefficient&#039;&#039;. Are these two identical? &amp;lt;br /&amp;gt;--[[User:Daniel Miksik|Daniel Miksik]] 07:16, 5 September 2008 (CDT)&lt;br /&gt;
:Yes, aggregation coefficient is synonymous with &amp;quot;weight&amp;quot; in this context.[[User:Nicolas Connault|Nicolas Connault]] 07:57, 8 September 2008 (CDT)&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Talk:Gradebook_1.9_Tutorial&amp;diff=43253</id>
		<title>Talk:Gradebook 1.9 Tutorial</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Talk:Gradebook_1.9_Tutorial&amp;diff=43253"/>
		<updated>2008-09-08T12:54:24Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* No Item Weight setting for categories */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;Strategy 1: Items weighted by their point values&lt;br /&gt;
&lt;br /&gt;
I would call this Strategy &amp;quot;Unweighted&amp;quot; since the weight of each item is 1.  I guess this is what Moodle means by &amp;quot;simple weighted&amp;quot;&lt;br /&gt;
&lt;br /&gt;
== No Item Weight setting for categories ==&lt;br /&gt;
&lt;br /&gt;
In my Moodle 1.9.2 I can see no &#039;&#039;Item Weight&#039;&#039; setting for grade categories (it&#039;s mentioned in the tutorial). But there is an item called &#039;&#039;Aggregation coefficient&#039;&#039;. Are these two identical? &amp;lt;br /&amp;gt;--[[User:Daniel Miksik|Daniel Miksik]] 07:16, 5 September 2008 (CDT)&lt;br /&gt;
|Yes, aggregation coefficient is synonymous with &amp;quot;weight&amp;quot; in this context.&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Portfolio_API&amp;diff=42234</id>
		<title>Development:Portfolio API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Portfolio_API&amp;diff=42234"/>
		<updated>2008-08-18T07:55:12Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: typos&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle_2.0}}This page describes the specification for a future feature, currently being worked on for [[Roadmap|Moodle 2.0]].  This spec is STILL UNDER CONSTRUCTION.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The Portfolio API is a core set of interfaces that all Moodle code will/should use so that we can easily publish files to all kinds of external document repository systems.&lt;br /&gt;
&lt;br /&gt;
It&#039;s important to remember that portfolios are generally treated as WRITE-ONLY.  All we are doing in Moodle is grabbing stuff and pushing it out to somewhere.  Management of the files and further combining/reflecting is done through the native interface provided by the portfolio system.  Reading of files from a repository is handled by the [[Development:Repository API|Repository API]].&lt;br /&gt;
&lt;br /&gt;
A typical user story:&lt;br /&gt;
&lt;br /&gt;
# When portfolios are enabled, every page or major piece of content in Moodle has a little &amp;quot;Save&amp;quot; button beside it.&lt;br /&gt;
# User clicks one of these buttons&lt;br /&gt;
# User is able to choose from a list of configured portfolios (this step will be skipped if there&#039;s only one).&lt;br /&gt;
# User may be asked to define the format of the captured content (eg pdf, IMS LD, HTML, XML ...)&lt;br /&gt;
# User may be asked to define some metadata to go with the captured content (some will be generated automatically).&lt;br /&gt;
# The content and metadata is COPIED to the external portfolio system&lt;br /&gt;
# User has an option to &amp;quot;Return to the page you left&amp;quot; or &amp;quot;Visit their portfolio&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Note this will be just as useful for teachers as for students.&lt;br /&gt;
&lt;br /&gt;
The formatting possibilities will vary depending on the context of the button and the type of external portfolios.  So for example, the &amp;quot;Save&amp;quot; button on the course page would allow the user to capture the whole course in IMS LD or Moodle backup format, which you would not have on a forum page.&lt;br /&gt;
&lt;br /&gt;
==Architecture==&lt;br /&gt;
&lt;br /&gt;
Here is how it will work:&lt;br /&gt;
&lt;br /&gt;
===Plugins and libraries===&lt;br /&gt;
&lt;br /&gt;
There will be two separate types of plugins &lt;br /&gt;
&lt;br /&gt;
# Portfolio (eg Mahara/Elgg/OSP/Facebook/Download) - this will be portfolio/type/xxx&lt;br /&gt;
# Transport (eg mnet/http/scp/cp/dav etc) - these will be reused across different portfolio plugins&lt;br /&gt;
&lt;br /&gt;
Then there will be different formats that plugins will support (and the part of moodle exporting content must support as well), eg IMS, moodle native, mahara native, pdf, encrypted pdf. These will have good libraries supporting them.&lt;br /&gt;
&lt;br /&gt;
The reason for abstracting the transport layer to its own plugin type is that it&#039;s pretty likely that the same external system might talk both portfolio and repository.&lt;br /&gt;
&lt;br /&gt;
===Admin===&lt;br /&gt;
&lt;br /&gt;
It is important to be allowed to have multiple instances of (some) plugins.  The workflow for adding a new one is:&lt;br /&gt;
&lt;br /&gt;
*Admin navigates to portfolio config&lt;br /&gt;
*Selects from the list of available portfolio plugins, and clicks &#039;add a new external portfolio&#039; (some may be disabled if there is an instance already and the plugin doesn&#039;t support multiple instances)&lt;br /&gt;
*Configure the plugin - select which transport and content types to use if there are multiple supported and installed, urls, authentication keys, etc.&lt;br /&gt;
*Set permissions (maybe) - handled by roles.&lt;br /&gt;
&lt;br /&gt;
This is not necessary for every type of portfolio, because many will just require the user to authenticate directly and if we do ever want to retain settings for each user we just use user preferences.&lt;br /&gt;
&lt;br /&gt;
===Exporting===&lt;br /&gt;
&lt;br /&gt;
*User is viewing a page that calls portfolio_add_button().  This checks to see if there are any configured portfolio plugin instances, and also (maybe) any permissions related to portfolios, and what the user&#039;s portfolio settings are, and then displays either a single &#039;add to portfolio&#039; button, or a drop down menu of the available systems with the add button.&lt;br /&gt;
*When this button is pressed, the user is redirected to portfolio/add.php, with some post data containing the responsible area (activity module or something like course or blog) callback file and function.&lt;br /&gt;
*On this page, the user is presented with a form to enter metadata about the item, and configure any options. At this point if there are multiple formats available for export (based on the intersection of what the plugin and module support), the user can select which format they want.  The plugin and module can both export mform elements for this page.  The user can at this point also select to send the data and wait (with a warning it might take awhile), or queue it for processing if it&#039;s larger.&lt;br /&gt;
*When the user has submitted the form, they are displayed a summary of what they&#039;re about to export, with &#039;confirm&#039; and &#039;edit&#039; buttons.  Edit just relaunches the form, and confirm goes to the next step.&lt;br /&gt;
*At this point, the portfolio plugin (or transport plugin) might need to take control for a step. For example, facebook or flickr might require the user to log in for the first time and confirm moodle is allowed to access their API&lt;br /&gt;
*When the user has confirmed their summary, a &#039;portfolio_send&#039; event will be triggered.  At this point, one of two things happen.  &lt;br /&gt;
# If the user has elected to wait, the &#039;instant&#039; event is fired, and when the caller gets control again, it displays the status to the user.&lt;br /&gt;
# If the user has elected to queue, the delayed event is fired and the user is notified.&lt;br /&gt;
*The user is given the option to continue to their portfolio, or return to where they were&lt;br /&gt;
*&lt;br /&gt;
*When the event is handled (either through the cron or instant event), the following happens:&lt;br /&gt;
*The event handler is invoked. This is almost certainly going to be a function in lib/portfoliolib.php rather than any handler in the portfolio plugins.  The event handler in turn calls the original callback that was passed to portfolio/add.php (module//lib.php or course/lib.php or something), which prepares the data in the format the user selected and returns control to the event handler.&lt;br /&gt;
*At this point we have all the metadata we need, and we have the content to send, so it&#039;s time to start transport negotiation, which happens in stages. Note that some plugins might not implement all stages, and 3 and 4 might be interchangeable in order, depending on the external system.&lt;br /&gt;
#request&lt;br /&gt;
#authentication&lt;br /&gt;
#content&lt;br /&gt;
#metadata (some control is passed to the portfolio plugin for packaging - either to just write out an xml file and zip up the whole package, or send a series of requests)&lt;br /&gt;
#completion&lt;br /&gt;
*When this is complete, we return control to the event handler (which, if it&#039;s an &#039;instant&#039; one, will return true to the caller, which will be the ajax request)&lt;br /&gt;
&lt;br /&gt;
===Storage===&lt;br /&gt;
&lt;br /&gt;
Obviously during this process, state is going to be lost between webserver requests and also between user input and event handling. Some data will be stored in the user&#039;s session (the result of the metadata form, for example) and some in temporary files on the file system.  These will be stored in dataroot/temp/portfolio/$userid-$timestamp/&lt;br /&gt;
&lt;br /&gt;
===Access/Permissions===&lt;br /&gt;
* Portfolio plugin must be allocatable per context (site/course/group etc)&lt;br /&gt;
* How can we assume all users will have an account on the remote system? We could try and silently authenticate them (or at least see if it&#039;s possible to) in the background at the first call to portfolio_add_button (and then cache the result in the session)&lt;br /&gt;
* Check MNet policy - it assumes everyone with the role can have an account (assuming they_sso_in or whatever it&#039;s called is on) - this doesn&#039;t scale though, other transport mechanisms and or plugin types might not follow this assumption.  I think this really must be defined by the portfolio plugin only, not the transport plugin.&lt;br /&gt;
&lt;br /&gt;
===Event API===&lt;br /&gt;
&lt;br /&gt;
It is probably worth using the event api to handle the sending of data, mostly just because it provides a handy way of detached processing.   However, any portfolio_send event will be &#039;owned&#039; by particular &amp;quot;instances&amp;quot; of plugins - and each plugin that subscribes to portfolio_send event (all of them by definition) will have to check if the event data belongs to any of their instances, which is potentially quite messy.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Technical==&lt;br /&gt;
&lt;br /&gt;
===Abstract Portfolio Baseclass: portfolio_plugin_base===&lt;br /&gt;
&lt;br /&gt;
Mixes providing some basic functionality by means of its own functions, with a number of abstract functions plugins must implement, and with some functions that plugins can also optionally override.&lt;br /&gt;
&lt;br /&gt;
See also: [[Development:Writing_a_Portfolio_Plugin]] for a full list of all methods you must/can/shouldn&#039;t override, as well as associated instructions for what else you need to do to create a new portfolio plugin&lt;br /&gt;
&lt;br /&gt;
===Abstract Caller Baseclass : portfolio_caller_base===&lt;br /&gt;
&lt;br /&gt;
Whenever somewhere in Moodle wants an &#039;add to portfolio&#039; button, they must subclass this.&lt;br /&gt;
&lt;br /&gt;
See also: [[Development:Adding_a_Portfolio_Button_to_a_page]] for a full list of all methods you must/can/shouldn&#039;t override as well as the associated instructions for how to call portfolio_add_button.&lt;br /&gt;
&lt;br /&gt;
===Database Tables===&lt;br /&gt;
&lt;br /&gt;
The actual information about plugins that are installed is just stored in mdl_config.&lt;br /&gt;
&lt;br /&gt;
Additionally, as we&#039;re configuring instances of plugins, rather than just one config set per plugin, we&#039;re not using mdl_config_plugin, but instead our own set of tables:&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;portfolio_instance:&#039;&#039;&#039;&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;
|-&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Datatype&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|integer&lt;br /&gt;
|sequence&lt;br /&gt;
|-&lt;br /&gt;
|plugin&lt;br /&gt;
|varchar(50)&lt;br /&gt;
|name of plugin (should match directory in portfolio/type)&lt;br /&gt;
|-&lt;br /&gt;
|name&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|name of this plugin instance&lt;br /&gt;
|-&lt;br /&gt;
|visible&lt;br /&gt;
|smallint&lt;br /&gt;
|0 or 1&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;portfolio_instance_config:&#039;&#039;&#039;&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;
|-&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Datatype&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|integer&lt;br /&gt;
|sequence&lt;br /&gt;
|-&lt;br /&gt;
|instance&lt;br /&gt;
|integer&lt;br /&gt;
|(pseudo)fk to portfolio_instance&lt;br /&gt;
|-&lt;br /&gt;
|name&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|config name&lt;br /&gt;
|-&lt;br /&gt;
|value&lt;br /&gt;
|text&lt;br /&gt;
|config value&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;portfolio_instance_user:&#039;&#039;&#039;&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;
|-&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Datatype&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|integer&lt;br /&gt;
|sequence&lt;br /&gt;
|-&lt;br /&gt;
|instance&lt;br /&gt;
|integer&lt;br /&gt;
|(pseudo)fk to portfolio_instance&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|integer&lt;br /&gt;
|(pseudo)fk to mdl_user&lt;br /&gt;
|-&lt;br /&gt;
|name&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|config name&lt;br /&gt;
|-&lt;br /&gt;
|value&lt;br /&gt;
|text&lt;br /&gt;
|config value&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;portfolio_log:&#039;&#039;&#039;&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;
|-&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Datatype&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|integer&lt;br /&gt;
|sequence&lt;br /&gt;
|-&lt;br /&gt;
|userid&lt;br /&gt;
|integer&lt;br /&gt;
|(pseudo) fk to mdl_user&lt;br /&gt;
|-&lt;br /&gt;
|time&lt;br /&gt;
|integer&lt;br /&gt;
|unix timestamp of transfer&lt;br /&gt;
|-&lt;br /&gt;
|portfolio&lt;br /&gt;
|integer&lt;br /&gt;
|(pseudo) fk to mdl_portfolio_instance&lt;br /&gt;
|-&lt;br /&gt;
|caller_class&lt;br /&gt;
|varchar(150)&lt;br /&gt;
|name of caller class (used in the case of duplicates to display information)&lt;br /&gt;
|-&lt;br /&gt;
|caller_file&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|file that contains the definition of caller_class&lt;br /&gt;
|-&lt;br /&gt;
|caller_sha1&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|sha1 information of export&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;portfolio_tempdata&#039;&#039;&#039;&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;
|-&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Datatype&#039;&#039;&#039;&lt;br /&gt;
|&#039;&#039;&#039;Comment&#039;&#039;&#039;&lt;br /&gt;
|-&lt;br /&gt;
|id&lt;br /&gt;
|integer&lt;br /&gt;
|sequence&lt;br /&gt;
|-&lt;br /&gt;
|data&lt;br /&gt;
|text&lt;br /&gt;
|serialized representation of export data&lt;br /&gt;
|-&lt;br /&gt;
|expirytime&lt;br /&gt;
|integer&lt;br /&gt;
|time this data (and the transfer) expires (and the record (and associated files)) will be deleted&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
All plugins can also implement their own database tables as needed, by creating a db/install.xml and db/upgrade.php inside portfolio/type/xxx/ (See [[Development:Writing_a_Portfolio_Plugin]]) for more information.&lt;br /&gt;
&lt;br /&gt;
===Portfolio Plugins===&lt;br /&gt;
&lt;br /&gt;
#[[Development:Mahara_Portfolio_Plugin|mahara]] (will be done for the initial implementation)&lt;br /&gt;
#download (should be done for the initial implementation) &lt;br /&gt;
&lt;br /&gt;
transport types and formats should be able to be found in a shared location for multiple plugins of both portfolio and repository type to use, but also might be specific to one type of plugin which means that moodle should support looking in multiple locations for these plugins. (eg mahara native format will be in the mahara portfolio plugin, but pdf format will be in a shared library)&lt;br /&gt;
&lt;br /&gt;
===Possible Transport Types===&lt;br /&gt;
&lt;br /&gt;
# mnet (will be done for the initial implementation as part of the Mahara Portfolio Plugin)&lt;br /&gt;
# download (will be done for the initial implementation as part of the Download Portfolio Plugin)&lt;br /&gt;
# http&lt;br /&gt;
# filesystem (could be local/nfs/samba whatever) (cp)&lt;br /&gt;
# ssh based (eg scp - should find and re-use the elgg block code as it deals with using ssh keys nicely)&lt;br /&gt;
# webdav&lt;br /&gt;
# open social? (http://code.google.com/apis/opensocial/)&lt;br /&gt;
&lt;br /&gt;
===Possible Export Formats===&lt;br /&gt;
&lt;br /&gt;
(Note that we don&#039;t necessarily want more than one of these for the initial implementation)&lt;br /&gt;
&lt;br /&gt;
# html&lt;br /&gt;
# pdf&lt;br /&gt;
# encrypted pdf&lt;br /&gt;
# ims?&lt;br /&gt;
# leap/piop? (http://wiki.cetis.ac.uk/LEAP_2.0) -- looking increasingly possible Mahara will speak this, so hopefully for the intial implementation&lt;br /&gt;
# moodle native?&lt;br /&gt;
# mahara native?&lt;br /&gt;
# Dublin Core (Enovation implemented this)&lt;br /&gt;
 &lt;br /&gt;
===Testing===&lt;br /&gt;
&lt;br /&gt;
Nico to help (see Development Approach for TDD-related approach)&lt;br /&gt;
&lt;br /&gt;
==MNET==&lt;br /&gt;
&lt;br /&gt;
This section has moved to [[Development:MNET_Roadmap]]&lt;br /&gt;
&lt;br /&gt;
==Duplication of Data==&lt;br /&gt;
&lt;br /&gt;
Moodle will keep track of what content it transfers and when.  It&#039;ll remember the url of the original export location (alphabetisising the url parameters and removing the sessionkey) and a sha1 hash of the data, so that if the user tries to export the same content, Moodle can warn the user (along with a notification of whether it has changed or not).&lt;br /&gt;
&lt;br /&gt;
At this point we can build support into the API for plugins to offer to differentiate between &#039;replace&#039; and &#039;add&#039;, but won&#039;t implement it for any we write for the initial implementation.&lt;br /&gt;
&lt;br /&gt;
==Assumptions==&lt;br /&gt;
&lt;br /&gt;
* Using Moodle&#039;s built in Event API&lt;br /&gt;
&lt;br /&gt;
==Save points in Moodle==&lt;br /&gt;
&lt;br /&gt;
moved to http://tracker.moodle.org/browse/MDL-15758 during development&lt;br /&gt;
&lt;br /&gt;
==Outstanding questions==&lt;br /&gt;
&lt;br /&gt;
*Dependance of transport plugins - eg filesystem might depend on http for sending request/finished pings&lt;br /&gt;
&lt;br /&gt;
==Development Approach==&lt;br /&gt;
* create two base classes&lt;br /&gt;
* create mahara portfolio plugin, relying on mnet&lt;br /&gt;
* create portfolio/add.php and portfolio/lib.php&lt;br /&gt;
* start adding calls to the portfolio_add_button function to various places in moodle, with their callbacks.&lt;br /&gt;
* create a dummy listener in mahara until the mahara side is specified&lt;br /&gt;
&lt;br /&gt;
After a conversation with Nico about testing, we decided the most sensible ordering to start with would be:&lt;br /&gt;
&lt;br /&gt;
# Write portfolio/add.php&lt;br /&gt;
# Write *skeleton functions* to support it in portfolio/lib.php and the class structure&lt;br /&gt;
# Write tests for those methods that are necessary&lt;br /&gt;
# Implement the functions to make the tests pass&lt;br /&gt;
# Keep adding module implementations&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
* [[Development:Repository API]]&lt;br /&gt;
* [[Development:File API]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Adding_a_Portfolio_Button_to_a_page&amp;diff=42092</id>
		<title>Development:Adding a Portfolio Button to a page</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Adding_a_Portfolio_Button_to_a_page&amp;diff=42092"/>
		<updated>2008-08-14T14:30:18Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Highlighting PHP code&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
Adding an &#039;Add to Portfolio&#039; button to any page is relatively trivial, there are just two things that you need to do:&lt;br /&gt;
&lt;br /&gt;
==Write a subclass==&lt;br /&gt;
&lt;br /&gt;
You can either subclass portfolio_caller_base for the general case, or portfolio_module_caller_base if you&#039;re somewhere inside mod/&lt;br /&gt;
&lt;br /&gt;
This sounds scary, but really it&#039;s not! It&#039;s a very small class.   portfolio_caller_base has abstract functions that you &#039;&#039;&#039;must&#039;&#039;&#039; override, and some functions that you &#039;&#039;&#039;can&#039;&#039;&#039; override if you want to do something special.  You should call it something like $module_portfolio_caller, or in a more complicated case (say assignment/type/upload, assignment_upload_portfolio_caller)&lt;br /&gt;
&lt;br /&gt;
If you&#039;re adding the portfolio button somewhere in a module, it&#039;s better to subclass portfolio_module_caller_base, which implements 2 of the below abstract methods for you.&lt;br /&gt;
&lt;br /&gt;
===Methods you must override===&lt;br /&gt;
&lt;br /&gt;
=====__construct=====&lt;br /&gt;
&lt;br /&gt;
When your object is constructed,  the contents of whatever callback arguments you passed to portfolio_add_button are passed back to you here in an array, so your chance to set member variables or do whatever you need is in the constructor.&lt;br /&gt;
&lt;br /&gt;
=====get_navigation=====&lt;br /&gt;
&lt;br /&gt;
During the export screens, it&#039;s desirable to still have some sensible navigation that logically follows from the place the user was before they started the export process.  This function should return components to pass to build_nagivation (extralinks and cm). &#039;&#039;&#039;portfolio_module_caller_base implements this for you&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====prepare_package=====&lt;br /&gt;
&lt;br /&gt;
prepares the package up before control is passed to the portfolio plugin. You should copy any files (or write out any files) into the temporary directory provided, where they&#039;ll be found by the portfolio plugin&lt;br /&gt;
&lt;br /&gt;
See also [[Development:Adding_a_Portfolio_Button_to_a_page#setting portfolio internal]]&lt;br /&gt;
&lt;br /&gt;
=====expected_time=====&lt;br /&gt;
&lt;br /&gt;
You should return a constant here to indicate how long the transfer is expected to take. This should be based on the size of the file.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH.&lt;br /&gt;
The first means the user will not be asked if they want to wait for the transfer or not, they will just wait.  The second and third mean they&#039;ll be given the option (and in the case of the third, advised not to). &lt;br /&gt;
The portfolio plugin can override this if it wants (eg in the case of download, they always want to wait for the transfer)&lt;br /&gt;
&lt;br /&gt;
=====check_permissions=====&lt;br /&gt;
&lt;br /&gt;
portfolio/add.php will expect the caller to verify the user is allowed to export the given content.  This function should perform any has_capability checks it needs to and return a booelan.&lt;br /&gt;
&lt;br /&gt;
=====get_return_url=====&lt;br /&gt;
&lt;br /&gt;
This is used for redirecting the user in the case of  a cancelled export, or at the end of their export, they are offered the option of continuing back to where they were (what this function returns) or on to their portfolio. &#039;&#039;&#039;portfolio_module_caller_base implements this for you (but will use mod/modname/view.php)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====display_name (static)=====&lt;br /&gt;
&lt;br /&gt;
A nice language string for displaying the location of this export to the user (this is used to notify the user in case of duplicate exports that originated from different places in moodle (Eg exporting an assignment upload and a forum post attachment that are the same file)&lt;br /&gt;
&lt;br /&gt;
=====get_sha1=====&lt;br /&gt;
&lt;br /&gt;
Return a sha1 of the content being exported - used to detect duplicate exports later.&lt;br /&gt;
&lt;br /&gt;
===Methods you can override===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====supported_formats (static)=====&lt;br /&gt;
&lt;br /&gt;
The formats this caller can support. At export time, both the plugin and the caller are polled for which formats they can support, and then the intersection is used to determine the export format. In the case that the intersection is greater than 1, the user is asked for their selection.&lt;br /&gt;
&lt;br /&gt;
The available formats you can choose from are in portfolio_supported_formats and are constants PORTFOLIO_FORMAT_XXX.  By default, the subclass defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
=====has_export_config=====&lt;br /&gt;
&lt;br /&gt;
If there&#039;s any addition config during the export process (for example, extra metadata), you can override this function to return true. If you do this, you must also override export_config_form and get_export_summary.&lt;br /&gt;
&lt;br /&gt;
=====export_config_form=====&lt;br /&gt;
&lt;br /&gt;
This function is called, and passed a moodle form object by reference to add elements to it.&lt;br /&gt;
&lt;br /&gt;
====export_config_validation====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_export_config====&lt;br /&gt;
&lt;br /&gt;
If at any point, your caller is going to use set_export_config, you must implement this function to return an array of allowed config fields. (Note that you can set export time config even if you&#039;re not using interactive user config)&lt;br /&gt;
&lt;br /&gt;
====get_export_summary====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_export_config, you must implement this to display nicely to the user on the confirmation screen.  It should return a named array (keys are nice strings to describe the config, values are the config options)&lt;br /&gt;
&lt;br /&gt;
==Call portfolio_add_button in the appropriate place==&lt;br /&gt;
&lt;br /&gt;
Now that you&#039;ve implemented this class, you just need to add the button.  To do this, you require_once(&amp;quot;$CFG-&amp;gt;libdir/portfoliolib.php&amp;quot;); and call portfolio_add_button.  It takes the following parameters:&lt;br /&gt;
&lt;br /&gt;
=====$callbackclass===== &lt;br /&gt;
&lt;br /&gt;
The name of the class you&#039;ve made that subclassed portfolio_caller_base.&lt;br /&gt;
&lt;br /&gt;
=====$callbackargs=====&lt;br /&gt;
&lt;br /&gt;
An associative array of key=&amp;gt;value pairs you want passed to the constructor of your class.  These &#039;&#039;&#039;must&#039;&#039;&#039; be primitives, as they are added as hidden form fields and cleaned to either PARAM_ALPHAEXT, PARAM_NUMERIC or PARAM_PATH.  Weird stuff will happen if they&#039;re not compliant.&lt;br /&gt;
&lt;br /&gt;
=====$callbackfile=====&lt;br /&gt;
&lt;br /&gt;
This can be autodetected from the backtrace of where this function was called, but if your class definition isn&#039;t in the same file as the caller (eg if your caller is some .php script, but the class is in a lib.php file), you can pass it explicitly here.&lt;br /&gt;
&lt;br /&gt;
=====$fullform=====&lt;br /&gt;
&lt;br /&gt;
whether you want the full form with the dropmenu of available plugins or just a little icon.  defaults to true. (using the icon will force a whole screen on the wizard)&lt;br /&gt;
&lt;br /&gt;
=====$return=====&lt;br /&gt;
&lt;br /&gt;
Whether you want the output returned or echoed. Defaults to false (echo)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Other ways to integrate==&lt;br /&gt;
&lt;br /&gt;
If it&#039;s undesirable to add a form, there are a couple of things you can do. For an example, see the export tab of the &#039;data&#039; module, which has already an export form, but just adds an option to export to portfolio rather than file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require_once($CFG-&amp;gt;libdir . &#039;/portfoliolib.php&#039;);&lt;br /&gt;
if (has_capability(&#039;mod/data:exportallentries&#039;, get_context_instance(CONTEXT_MODULE, $this-&amp;gt;_cm-&amp;gt;id))) {&lt;br /&gt;
  if ($portfoliooptions = portfolio_instance_select(portfolio_instances(),&lt;br /&gt;
          call_user_func(array(&#039;data_portfolio_caller&#039;, &#039;supported_formats&#039;)),&lt;br /&gt;
          &#039;data_portfolio_caller&#039;, &#039;&#039;, true, true)) {&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;header&#039;, &#039;notice&#039;, get_string(&#039;portfolionotfile&#039;, &#039;data&#039;) . &#039;:&#039;);&lt;br /&gt;
    $portfoliooptions[0] = get_string(&#039;none&#039;);&lt;br /&gt;
    ksort($portfoliooptions);&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;select&#039;, &#039;portfolio&#039;, &lt;br /&gt;
       get_string(&#039;portfolio&#039;, &#039;portfolio&#039;), $portfoliooptions);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code adds a select option to the existing export form containing the available portfolio instances.&lt;br /&gt;
&lt;br /&gt;
Then in the form handler:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (array_key_exists(&#039;portfolio&#039;, $formdata) &amp;amp;&amp;amp; !empty($formdata[&#039;portfolio&#039;])) {&lt;br /&gt;
  // fake  portfolio callback stuff and redirect&lt;br /&gt;
  $formdata[&#039;id&#039;] = $cm-&amp;gt;id;&lt;br /&gt;
  $formdata[&#039;exporttype&#039;] = &#039;csv&#039;; // force for now&lt;br /&gt;
  $url = portfolio_fake_add_url($formdata[&#039;portfolio&#039;], &#039;data_portfolio_caller&#039;, &lt;br /&gt;
                                &#039;/mod/data/lib.php&#039;, $formdata);&lt;br /&gt;
  redirect($url);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The portfolio_fake_add_url function returns the url that you need to redirect to, that would normally be the result of portfolio_add_button form being submitted.&lt;br /&gt;
&lt;br /&gt;
==A few extra notes on the caller base class==&lt;br /&gt;
&lt;br /&gt;
===protected $course===&lt;br /&gt;
There is a protected member variable, $course, that subclasses can set (with $this-&amp;gt;set(&#039;course&#039;, $course); ).&lt;br /&gt;
&lt;br /&gt;
portfolio_add_button tries to look for a course object at the point that it&#039;s called, by doing a global $COURSE which is hackish but mostly works. This is also used to build the navigation during the export process.  If for some reason, your navigation doesn&#039;t include the current course, you can set it like this. You shouldn&#039;t need to in the majority of cases.&lt;br /&gt;
&lt;br /&gt;
===serialization===&lt;br /&gt;
The caller object is stored in the database in serialized form.  The portfolio code works around this by loading the class definitions and then serializing and unserializing the objects again.  However, if you&#039;re storing any real objects in your caller class, you will need to do this as well.  See the assignment implementation for how this done (using php5&#039;s __wakeup function):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function __wakeup() {&lt;br /&gt;
    require_once($this-&amp;gt;assignmentfile);&lt;br /&gt;
    $this-&amp;gt;assignment = unserialize(serialize($this-&amp;gt;assignment));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===setting portfolio internal===&lt;br /&gt;
Sometimes during prepare_package, you need to call functions in the libraries to render content as HTML that would normally also call portfolio_add_button.  This will result in &#039;you already have an export active in this session&#039; error - to get around this, do something like&lt;br /&gt;
 &lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
define(&#039;PORTFOLIO_INTERNAL&#039;, true);&lt;br /&gt;
modulename_print_some_htmlcontent();&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Adding_a_Portfolio_Button_to_a_page&amp;diff=42091</id>
		<title>Development:Adding a Portfolio Button to a page</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Adding_a_Portfolio_Button_to_a_page&amp;diff=42091"/>
		<updated>2008-08-14T14:29:55Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Highlighting PHP code&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
Adding an &#039;Add to Portfolio&#039; button to any page is relatively trivial, there are just two things that you need to do:&lt;br /&gt;
&lt;br /&gt;
==Write a subclass==&lt;br /&gt;
&lt;br /&gt;
You can either subclass portfolio_caller_base for the general case, or portfolio_module_caller_base if you&#039;re somewhere inside mod/&lt;br /&gt;
&lt;br /&gt;
This sounds scary, but really it&#039;s not! It&#039;s a very small class.   portfolio_caller_base has abstract functions that you &#039;&#039;&#039;must&#039;&#039;&#039; override, and some functions that you &#039;&#039;&#039;can&#039;&#039;&#039; override if you want to do something special.  You should call it something like $module_portfolio_caller, or in a more complicated case (say assignment/type/upload, assignment_upload_portfolio_caller)&lt;br /&gt;
&lt;br /&gt;
If you&#039;re adding the portfolio button somewhere in a module, it&#039;s better to subclass portfolio_module_caller_base, which implements 2 of the below abstract methods for you.&lt;br /&gt;
&lt;br /&gt;
===Methods you must override===&lt;br /&gt;
&lt;br /&gt;
=====__construct=====&lt;br /&gt;
&lt;br /&gt;
When your object is constructed,  the contents of whatever callback arguments you passed to portfolio_add_button are passed back to you here in an array, so your chance to set member variables or do whatever you need is in the constructor.&lt;br /&gt;
&lt;br /&gt;
=====get_navigation=====&lt;br /&gt;
&lt;br /&gt;
During the export screens, it&#039;s desirable to still have some sensible navigation that logically follows from the place the user was before they started the export process.  This function should return components to pass to build_nagivation (extralinks and cm). &#039;&#039;&#039;portfolio_module_caller_base implements this for you&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====prepare_package=====&lt;br /&gt;
&lt;br /&gt;
prepares the package up before control is passed to the portfolio plugin. You should copy any files (or write out any files) into the temporary directory provided, where they&#039;ll be found by the portfolio plugin&lt;br /&gt;
&lt;br /&gt;
See also [[Development:Adding_a_Portfolio_Button_to_a_page#setting portfolio internal]]&lt;br /&gt;
&lt;br /&gt;
=====expected_time=====&lt;br /&gt;
&lt;br /&gt;
You should return a constant here to indicate how long the transfer is expected to take. This should be based on the size of the file.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH.&lt;br /&gt;
The first means the user will not be asked if they want to wait for the transfer or not, they will just wait.  The second and third mean they&#039;ll be given the option (and in the case of the third, advised not to). &lt;br /&gt;
The portfolio plugin can override this if it wants (eg in the case of download, they always want to wait for the transfer)&lt;br /&gt;
&lt;br /&gt;
=====check_permissions=====&lt;br /&gt;
&lt;br /&gt;
portfolio/add.php will expect the caller to verify the user is allowed to export the given content.  This function should perform any has_capability checks it needs to and return a booelan.&lt;br /&gt;
&lt;br /&gt;
=====get_return_url=====&lt;br /&gt;
&lt;br /&gt;
This is used for redirecting the user in the case of  a cancelled export, or at the end of their export, they are offered the option of continuing back to where they were (what this function returns) or on to their portfolio. &#039;&#039;&#039;portfolio_module_caller_base implements this for you (but will use mod/modname/view.php)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====display_name (static)=====&lt;br /&gt;
&lt;br /&gt;
A nice language string for displaying the location of this export to the user (this is used to notify the user in case of duplicate exports that originated from different places in moodle (Eg exporting an assignment upload and a forum post attachment that are the same file)&lt;br /&gt;
&lt;br /&gt;
=====get_sha1=====&lt;br /&gt;
&lt;br /&gt;
Return a sha1 of the content being exported - used to detect duplicate exports later.&lt;br /&gt;
&lt;br /&gt;
===Methods you can override===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====supported_formats (static)=====&lt;br /&gt;
&lt;br /&gt;
The formats this caller can support. At export time, both the plugin and the caller are polled for which formats they can support, and then the intersection is used to determine the export format. In the case that the intersection is greater than 1, the user is asked for their selection.&lt;br /&gt;
&lt;br /&gt;
The available formats you can choose from are in portfolio_supported_formats and are constants PORTFOLIO_FORMAT_XXX.  By default, the subclass defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
=====has_export_config=====&lt;br /&gt;
&lt;br /&gt;
If there&#039;s any addition config during the export process (for example, extra metadata), you can override this function to return true. If you do this, you must also override export_config_form and get_export_summary.&lt;br /&gt;
&lt;br /&gt;
=====export_config_form=====&lt;br /&gt;
&lt;br /&gt;
This function is called, and passed a moodle form object by reference to add elements to it.&lt;br /&gt;
&lt;br /&gt;
====export_config_validation====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_export_config====&lt;br /&gt;
&lt;br /&gt;
If at any point, your caller is going to use set_export_config, you must implement this function to return an array of allowed config fields. (Note that you can set export time config even if you&#039;re not using interactive user config)&lt;br /&gt;
&lt;br /&gt;
====get_export_summary====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_export_config, you must implement this to display nicely to the user on the confirmation screen.  It should return a named array (keys are nice strings to describe the config, values are the config options)&lt;br /&gt;
&lt;br /&gt;
==Call portfolio_add_button in the appropriate place==&lt;br /&gt;
&lt;br /&gt;
Now that you&#039;ve implemented this class, you just need to add the button.  To do this, you require_once(&amp;quot;$CFG-&amp;gt;libdir/portfoliolib.php&amp;quot;); and call portfolio_add_button.  It takes the following parameters:&lt;br /&gt;
&lt;br /&gt;
=====$callbackclass===== &lt;br /&gt;
&lt;br /&gt;
The name of the class you&#039;ve made that subclassed portfolio_caller_base.&lt;br /&gt;
&lt;br /&gt;
=====$callbackargs=====&lt;br /&gt;
&lt;br /&gt;
An associative array of key=&amp;gt;value pairs you want passed to the constructor of your class.  These &#039;&#039;&#039;must&#039;&#039;&#039; be primitives, as they are added as hidden form fields and cleaned to either PARAM_ALPHAEXT, PARAM_NUMERIC or PARAM_PATH.  Weird stuff will happen if they&#039;re not compliant.&lt;br /&gt;
&lt;br /&gt;
=====$callbackfile=====&lt;br /&gt;
&lt;br /&gt;
This can be autodetected from the backtrace of where this function was called, but if your class definition isn&#039;t in the same file as the caller (eg if your caller is some .php script, but the class is in a lib.php file), you can pass it explicitly here.&lt;br /&gt;
&lt;br /&gt;
=====$fullform=====&lt;br /&gt;
&lt;br /&gt;
whether you want the full form with the dropmenu of available plugins or just a little icon.  defaults to true. (using the icon will force a whole screen on the wizard)&lt;br /&gt;
&lt;br /&gt;
=====$return=====&lt;br /&gt;
&lt;br /&gt;
Whether you want the output returned or echoed. Defaults to false (echo)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Other ways to integrate==&lt;br /&gt;
&lt;br /&gt;
If it&#039;s undesirable to add a form, there are a couple of things you can do. For an example, see the export tab of the &#039;data&#039; module, which has already an export form, but just adds an option to export to portfolio rather than file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require_once($CFG-&amp;gt;libdir . &#039;/portfoliolib.php&#039;);&lt;br /&gt;
if (has_capability(&#039;mod/data:exportallentries&#039;, get_context_instance(CONTEXT_MODULE, $this-&amp;gt;_cm-&amp;gt;id))) {&lt;br /&gt;
  if ($portfoliooptions = portfolio_instance_select(portfolio_instances(),&lt;br /&gt;
          call_user_func(array(&#039;data_portfolio_caller&#039;, &#039;supported_formats&#039;)),&lt;br /&gt;
          &#039;data_portfolio_caller&#039;, &#039;&#039;, true, true)) {&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;header&#039;, &#039;notice&#039;, get_string(&#039;portfolionotfile&#039;, &#039;data&#039;) . &#039;:&#039;);&lt;br /&gt;
    $portfoliooptions[0] = get_string(&#039;none&#039;);&lt;br /&gt;
    ksort($portfoliooptions);&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;select&#039;, &#039;portfolio&#039;, &lt;br /&gt;
       get_string(&#039;portfolio&#039;, &#039;portfolio&#039;), $portfoliooptions);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code adds a select option to the existing export form containing the available portfolio instances.&lt;br /&gt;
&lt;br /&gt;
Then in the form handler:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (array_key_exists(&#039;portfolio&#039;, $formdata) &amp;amp;&amp;amp; !empty($formdata[&#039;portfolio&#039;])) {&lt;br /&gt;
  // fake  portfolio callback stuff and redirect&lt;br /&gt;
  $formdata[&#039;id&#039;] = $cm-&amp;gt;id;&lt;br /&gt;
  $formdata[&#039;exporttype&#039;] = &#039;csv&#039;; // force for now&lt;br /&gt;
  $url = portfolio_fake_add_url($formdata[&#039;portfolio&#039;], &#039;data_portfolio_caller&#039;, &lt;br /&gt;
                                &#039;/mod/data/lib.php&#039;, $formdata);&lt;br /&gt;
  redirect($url);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The portfolio_fake_add_url function returns the url that you need to redirect to, that would normally be the result of portfolio_add_button form being submitted.&lt;br /&gt;
&lt;br /&gt;
==A few extra notes on the caller base class==&lt;br /&gt;
&lt;br /&gt;
===protected $course===&lt;br /&gt;
There is a protected member variable, $course, that subclasses can set (with $this-&amp;gt;set(&#039;course&#039;, $course); ).&lt;br /&gt;
&lt;br /&gt;
portfolio_add_button tries to look for a course object at the point that it&#039;s called, by doing a global $COURSE which is hackish but mostly works. This is also used to build the navigation during the export process.  If for some reason, your navigation doesn&#039;t include the current course, you can set it like this. You shouldn&#039;t need to in the majority of cases.&lt;br /&gt;
&lt;br /&gt;
===serialization===&lt;br /&gt;
The caller object is stored in the database in serialized form.  The portfolio code works around this by loading the class definitions and then serializing and unserializing the objects again.  However, if you&#039;re storing any real objects in your caller class, you will need to do this as well.  See the assignment implementation for how this done (using php5&#039;s __wakeup function):&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
public function __wakeup() {&lt;br /&gt;
    require_once($this-&amp;gt;assignmentfile);&lt;br /&gt;
    $this-&amp;gt;assignment = unserialize(serialize($this-&amp;gt;assignment));&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===setting portfolio internal===&lt;br /&gt;
Sometimes during prepare_package, you need to call functions in the libraries to render content as HTML that would normally also call portfolio_add_button.  This will result in &#039;you already have an export active in this session&#039; error - to get around this, do something like&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    define(&#039;PORTFOLIO_INTERNAL&#039;, true);&lt;br /&gt;
    modulename_print_some_htmlcontent();&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Adding_a_Portfolio_Button_to_a_page&amp;diff=42090</id>
		<title>Development:Adding a Portfolio Button to a page</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Adding_a_Portfolio_Button_to_a_page&amp;diff=42090"/>
		<updated>2008-08-14T14:28:16Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Highlighting PHP code&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
Adding an &#039;Add to Portfolio&#039; button to any page is relatively trivial, there are just two things that you need to do:&lt;br /&gt;
&lt;br /&gt;
==Write a subclass==&lt;br /&gt;
&lt;br /&gt;
You can either subclass portfolio_caller_base for the general case, or portfolio_module_caller_base if you&#039;re somewhere inside mod/&lt;br /&gt;
&lt;br /&gt;
This sounds scary, but really it&#039;s not! It&#039;s a very small class.   portfolio_caller_base has abstract functions that you &#039;&#039;&#039;must&#039;&#039;&#039; override, and some functions that you &#039;&#039;&#039;can&#039;&#039;&#039; override if you want to do something special.  You should call it something like $module_portfolio_caller, or in a more complicated case (say assignment/type/upload, assignment_upload_portfolio_caller)&lt;br /&gt;
&lt;br /&gt;
If you&#039;re adding the portfolio button somewhere in a module, it&#039;s better to subclass portfolio_module_caller_base, which implements 2 of the below abstract methods for you.&lt;br /&gt;
&lt;br /&gt;
===Methods you must override===&lt;br /&gt;
&lt;br /&gt;
=====__construct=====&lt;br /&gt;
&lt;br /&gt;
When your object is constructed,  the contents of whatever callback arguments you passed to portfolio_add_button are passed back to you here in an array, so your chance to set member variables or do whatever you need is in the constructor.&lt;br /&gt;
&lt;br /&gt;
=====get_navigation=====&lt;br /&gt;
&lt;br /&gt;
During the export screens, it&#039;s desirable to still have some sensible navigation that logically follows from the place the user was before they started the export process.  This function should return components to pass to build_nagivation (extralinks and cm). &#039;&#039;&#039;portfolio_module_caller_base implements this for you&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====prepare_package=====&lt;br /&gt;
&lt;br /&gt;
prepares the package up before control is passed to the portfolio plugin. You should copy any files (or write out any files) into the temporary directory provided, where they&#039;ll be found by the portfolio plugin&lt;br /&gt;
&lt;br /&gt;
See also [[Development:Adding_a_Portfolio_Button_to_a_page#setting portfolio internal]]&lt;br /&gt;
&lt;br /&gt;
=====expected_time=====&lt;br /&gt;
&lt;br /&gt;
You should return a constant here to indicate how long the transfer is expected to take. This should be based on the size of the file.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH.&lt;br /&gt;
The first means the user will not be asked if they want to wait for the transfer or not, they will just wait.  The second and third mean they&#039;ll be given the option (and in the case of the third, advised not to). &lt;br /&gt;
The portfolio plugin can override this if it wants (eg in the case of download, they always want to wait for the transfer)&lt;br /&gt;
&lt;br /&gt;
=====check_permissions=====&lt;br /&gt;
&lt;br /&gt;
portfolio/add.php will expect the caller to verify the user is allowed to export the given content.  This function should perform any has_capability checks it needs to and return a booelan.&lt;br /&gt;
&lt;br /&gt;
=====get_return_url=====&lt;br /&gt;
&lt;br /&gt;
This is used for redirecting the user in the case of  a cancelled export, or at the end of their export, they are offered the option of continuing back to where they were (what this function returns) or on to their portfolio. &#039;&#039;&#039;portfolio_module_caller_base implements this for you (but will use mod/modname/view.php)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====display_name (static)=====&lt;br /&gt;
&lt;br /&gt;
A nice language string for displaying the location of this export to the user (this is used to notify the user in case of duplicate exports that originated from different places in moodle (Eg exporting an assignment upload and a forum post attachment that are the same file)&lt;br /&gt;
&lt;br /&gt;
=====get_sha1=====&lt;br /&gt;
&lt;br /&gt;
Return a sha1 of the content being exported - used to detect duplicate exports later.&lt;br /&gt;
&lt;br /&gt;
===Methods you can override===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====supported_formats (static)=====&lt;br /&gt;
&lt;br /&gt;
The formats this caller can support. At export time, both the plugin and the caller are polled for which formats they can support, and then the intersection is used to determine the export format. In the case that the intersection is greater than 1, the user is asked for their selection.&lt;br /&gt;
&lt;br /&gt;
The available formats you can choose from are in portfolio_supported_formats and are constants PORTFOLIO_FORMAT_XXX.  By default, the subclass defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
=====has_export_config=====&lt;br /&gt;
&lt;br /&gt;
If there&#039;s any addition config during the export process (for example, extra metadata), you can override this function to return true. If you do this, you must also override export_config_form and get_export_summary.&lt;br /&gt;
&lt;br /&gt;
=====export_config_form=====&lt;br /&gt;
&lt;br /&gt;
This function is called, and passed a moodle form object by reference to add elements to it.&lt;br /&gt;
&lt;br /&gt;
====export_config_validation====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_export_config====&lt;br /&gt;
&lt;br /&gt;
If at any point, your caller is going to use set_export_config, you must implement this function to return an array of allowed config fields. (Note that you can set export time config even if you&#039;re not using interactive user config)&lt;br /&gt;
&lt;br /&gt;
====get_export_summary====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_export_config, you must implement this to display nicely to the user on the confirmation screen.  It should return a named array (keys are nice strings to describe the config, values are the config options)&lt;br /&gt;
&lt;br /&gt;
==Call portfolio_add_button in the appropriate place==&lt;br /&gt;
&lt;br /&gt;
Now that you&#039;ve implemented this class, you just need to add the button.  To do this, you require_once(&amp;quot;$CFG-&amp;gt;libdir/portfoliolib.php&amp;quot;); and call portfolio_add_button.  It takes the following parameters:&lt;br /&gt;
&lt;br /&gt;
=====$callbackclass===== &lt;br /&gt;
&lt;br /&gt;
The name of the class you&#039;ve made that subclassed portfolio_caller_base.&lt;br /&gt;
&lt;br /&gt;
=====$callbackargs=====&lt;br /&gt;
&lt;br /&gt;
An associative array of key=&amp;gt;value pairs you want passed to the constructor of your class.  These &#039;&#039;&#039;must&#039;&#039;&#039; be primitives, as they are added as hidden form fields and cleaned to either PARAM_ALPHAEXT, PARAM_NUMERIC or PARAM_PATH.  Weird stuff will happen if they&#039;re not compliant.&lt;br /&gt;
&lt;br /&gt;
=====$callbackfile=====&lt;br /&gt;
&lt;br /&gt;
This can be autodetected from the backtrace of where this function was called, but if your class definition isn&#039;t in the same file as the caller (eg if your caller is some .php script, but the class is in a lib.php file), you can pass it explicitly here.&lt;br /&gt;
&lt;br /&gt;
=====$fullform=====&lt;br /&gt;
&lt;br /&gt;
whether you want the full form with the dropmenu of available plugins or just a little icon.  defaults to true. (using the icon will force a whole screen on the wizard)&lt;br /&gt;
&lt;br /&gt;
=====$return=====&lt;br /&gt;
&lt;br /&gt;
Whether you want the output returned or echoed. Defaults to false (echo)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Other ways to integrate==&lt;br /&gt;
&lt;br /&gt;
If it&#039;s undesirable to add a form, there are a couple of things you can do. For an example, see the export tab of the &#039;data&#039; module, which has already an export form, but just adds an option to export to portfolio rather than file:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
require_once($CFG-&amp;gt;libdir . &#039;/portfoliolib.php&#039;);&lt;br /&gt;
if (has_capability(&#039;mod/data:exportallentries&#039;, get_context_instance(CONTEXT_MODULE, $this-&amp;gt;_cm-&amp;gt;id))) {&lt;br /&gt;
  if ($portfoliooptions = portfolio_instance_select(portfolio_instances(),&lt;br /&gt;
          call_user_func(array(&#039;data_portfolio_caller&#039;, &#039;supported_formats&#039;)),&lt;br /&gt;
          &#039;data_portfolio_caller&#039;, &#039;&#039;, true, true)) {&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;header&#039;, &#039;notice&#039;, get_string(&#039;portfolionotfile&#039;, &#039;data&#039;) . &#039;:&#039;);&lt;br /&gt;
    $portfoliooptions[0] = get_string(&#039;none&#039;);&lt;br /&gt;
    ksort($portfoliooptions);&lt;br /&gt;
    $mform-&amp;gt;addElement(&#039;select&#039;, &#039;portfolio&#039;, &lt;br /&gt;
       get_string(&#039;portfolio&#039;, &#039;portfolio&#039;), $portfoliooptions);&lt;br /&gt;
  }&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This code adds a select option to the existing export form containing the available portfolio instances.&lt;br /&gt;
&lt;br /&gt;
Then in the form handler:&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
if (array_key_exists(&#039;portfolio&#039;, $formdata) &amp;amp;&amp;amp; !empty($formdata[&#039;portfolio&#039;])) {&lt;br /&gt;
  // fake  portfolio callback stuff and redirect&lt;br /&gt;
  $formdata[&#039;id&#039;] = $cm-&amp;gt;id;&lt;br /&gt;
  $formdata[&#039;exporttype&#039;] = &#039;csv&#039;; // force for now&lt;br /&gt;
  $url = portfolio_fake_add_url($formdata[&#039;portfolio&#039;], &#039;data_portfolio_caller&#039;, &lt;br /&gt;
                                &#039;/mod/data/lib.php&#039;, $formdata);&lt;br /&gt;
  redirect($url);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The portfolio_fake_add_url function returns the url that you need to redirect to, that would normally be the result of portfolio_add_button form being submitted.&lt;br /&gt;
&lt;br /&gt;
==A few extra notes on the caller base class==&lt;br /&gt;
&lt;br /&gt;
===protected $course===&lt;br /&gt;
There is a protected member variable, $course, that subclasses can set (with $this-&amp;gt;set(&#039;course&#039;, $course); ).&lt;br /&gt;
&lt;br /&gt;
portfolio_add_button tries to look for a course object at the point that it&#039;s called, by doing a global $COURSE which is hackish but mostly works. This is also used to build the navigation during the export process.  If for some reason, your navigation doesn&#039;t include the current course, you can set it like this. You shouldn&#039;t need to in the majority of cases.&lt;br /&gt;
&lt;br /&gt;
===serialization===&lt;br /&gt;
The caller object is stored in the database in serialized form.  The portfolio code works around this by loading the class definitions and then serializing and unserializing the objects again.  However, if you&#039;re storing any real objects in your caller class, you will need to do this as well.  See the assignment implementation for how this done (using php5&#039;s __wakeup function):&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    public function __wakeup() {&lt;br /&gt;
        require_once($this-&amp;gt;assignmentfile);&lt;br /&gt;
        $this-&amp;gt;assignment = unserialize(serialize($this-&amp;gt;assignment));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
===setting portfolio internal===&lt;br /&gt;
Sometimes during prepare_package, you need to call functions in the libraries to render content as HTML that would normally also call portfolio_add_button.  This will result in &#039;you already have an export active in this session&#039; error - to get around this, do something like&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    define(&#039;PORTFOLIO_INTERNAL&#039;, true);&lt;br /&gt;
    modulename_print_some_htmlcontent();&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Adding_a_Portfolio_Button_to_a_page&amp;diff=42084</id>
		<title>Development:Adding a Portfolio Button to a page</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Adding_a_Portfolio_Button_to_a_page&amp;diff=42084"/>
		<updated>2008-08-14T14:06:17Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* get_return_url */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
&lt;br /&gt;
Adding an &#039;Add to Portfolio&#039; button to any page is relatively trivial, there are just two things that you need to do:&lt;br /&gt;
&lt;br /&gt;
==Write a subclass==&lt;br /&gt;
&lt;br /&gt;
You can either subclass portfolio_caller_base for the general case, or portfolio_module_caller_base if you&#039;re somewhere inside mod/&lt;br /&gt;
&lt;br /&gt;
This sounds scary, but really it&#039;s not! It&#039;s a very small class.   portfolio_caller_base has abstract functions that you &#039;&#039;&#039;must&#039;&#039;&#039; override, and some functions that you &#039;&#039;&#039;can&#039;&#039;&#039; override if you want to do something special.  You should call it something like $module_portfolio_caller, or in a more complicated case (say assignment/type/upload, assignment_upload_portfolio_caller)&lt;br /&gt;
&lt;br /&gt;
If you&#039;re adding the portfolio button somewhere in a module, it&#039;s better to subclass portfolio_module_caller_base, which implements 2 of the below abstract methods for you.&lt;br /&gt;
&lt;br /&gt;
===Methods you must override===&lt;br /&gt;
&lt;br /&gt;
=====__construct=====&lt;br /&gt;
&lt;br /&gt;
When your object is constructed,  the contents of whatever callback arguments you passed to portfolio_add_button are passed back to you here in an array, so your chance to set member variables or do whatever you need is in the constructor.&lt;br /&gt;
&lt;br /&gt;
=====get_navigation=====&lt;br /&gt;
&lt;br /&gt;
During the export screens, it&#039;s desirable to still have some sensible navigation that logically follows from the place the user was before they started the export process.  This function should return components to pass to build_nagivation (extralinks and cm). &#039;&#039;&#039;portfolio_module_caller_base implements this for you&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====prepare_package=====&lt;br /&gt;
&lt;br /&gt;
prepares the package up before control is passed to the portfolio plugin. You should copy any files (or write out any files) into the temporary directory provided, where they&#039;ll be found by the portfolio plugin&lt;br /&gt;
&lt;br /&gt;
See also [[Development:Adding_a_Portfolio_Button_to_a_page#setting portfolio internal]]&lt;br /&gt;
&lt;br /&gt;
=====expected_time=====&lt;br /&gt;
&lt;br /&gt;
You should return a constant here to indicate how long the transfer is expected to take. This should be based on the size of the file.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH.&lt;br /&gt;
The first means the user will not be asked if they want to wait for the transfer or not, they will just wait.  The second and third mean they&#039;ll be given the option (and in the case of the third, advised not to). &lt;br /&gt;
The portfolio plugin can override this if it wants (eg in the case of download, they always want to wait for the transfer)&lt;br /&gt;
&lt;br /&gt;
=====check_permissions=====&lt;br /&gt;
&lt;br /&gt;
portfolio/add.php will expect the caller to verify the user is allowed to export the given content.  This function should perform any has_capability checks it needs to and return a booelan.&lt;br /&gt;
&lt;br /&gt;
=====get_return_url=====&lt;br /&gt;
&lt;br /&gt;
This is used for redirecting the user in the case of  a cancelled export, or at the end of their export, they are offered the option of continuing back to where they were (what this function returns) or on to their portfolio. &#039;&#039;&#039;portfolio_module_caller_base implements this for you (but will use mod/modname/view.php)&#039;&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
=====display_name (static)=====&lt;br /&gt;
&lt;br /&gt;
A nice language string for displaying the location of this export to the user (this is used to notify the user in case of duplicate exports that originated from different places in moodle (Eg exporting an assignment upload and a forum post attachment that are the same file)&lt;br /&gt;
&lt;br /&gt;
=====get_sha1=====&lt;br /&gt;
&lt;br /&gt;
Return a sha1 of the content being exported - used to detect duplicate exports later.&lt;br /&gt;
&lt;br /&gt;
===Methods you can override===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====supported_formats (static)=====&lt;br /&gt;
&lt;br /&gt;
The formats this caller can support. At export time, both the plugin and the caller are polled for which formats they can support, and then the intersection is used to determine the export format. In the case that the intersection is greater than 1, the user is asked for their selection.&lt;br /&gt;
&lt;br /&gt;
The available formats you can choose from are in portfolio_supported_formats and are constants PORTFOLIO_FORMAT_XXX.  By default, the subclass defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
=====has_export_config=====&lt;br /&gt;
&lt;br /&gt;
If there&#039;s any addition config during the export process (for example, extra metadata), you can override this function to return true. If you do this, you must also override export_config_form and get_export_summary.&lt;br /&gt;
&lt;br /&gt;
=====export_config_form=====&lt;br /&gt;
&lt;br /&gt;
This function is called, and passed a moodle form object by reference to add elements to it.&lt;br /&gt;
&lt;br /&gt;
====export_config_validation====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_export_config====&lt;br /&gt;
&lt;br /&gt;
If at any point, your caller is going to use set_export_config, you must implement this function to return an array of allowed config fields. (Note that you can set export time config even if you&#039;re not using interactive user config)&lt;br /&gt;
&lt;br /&gt;
====get_export_summary====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_export_config, you must implement this to display nicely to the user on the confirmation screen.  It should return a named array (keys are nice strings to describe the config, values are the config options)&lt;br /&gt;
&lt;br /&gt;
==Call portfolio_add_button in the appropriate place==&lt;br /&gt;
&lt;br /&gt;
Now that you&#039;ve implemented this class, you just need to add the button.  To do this, you require_once(&amp;quot;$CFG-&amp;gt;libdir/portfoliolib.php&amp;quot;); and call portfolio_add_button.  It takes the following parameters:&lt;br /&gt;
&lt;br /&gt;
=====$callbackclass===== &lt;br /&gt;
&lt;br /&gt;
The name of the class you&#039;ve made that subclassed portfolio_caller_base.&lt;br /&gt;
&lt;br /&gt;
=====$callbackargs=====&lt;br /&gt;
&lt;br /&gt;
An associative array of key=&amp;gt;value pairs you want passed to the constructor of your class.  These &#039;&#039;&#039;must&#039;&#039;&#039; be primitives, as they are added as hidden form fields and cleaned to either PARAM_ALPHAEXT, PARAM_NUMERIC or PARAM_PATH.  Weird stuff will happen if they&#039;re not compliant.&lt;br /&gt;
&lt;br /&gt;
=====$callbackfile=====&lt;br /&gt;
&lt;br /&gt;
This can be autodetected from the backtrace of where this function was called, but if your class definition isn&#039;t in the same file as the caller (eg if your caller is some .php script, but the class is in a lib.php file), you can pass it explicitly here.&lt;br /&gt;
&lt;br /&gt;
=====$fullform=====&lt;br /&gt;
&lt;br /&gt;
whether you want the full form with the dropmenu of available plugins or just a little icon.  defaults to true. (using the icon will force a whole screen on the wizard)&lt;br /&gt;
&lt;br /&gt;
=====$return=====&lt;br /&gt;
&lt;br /&gt;
Whether you want the output returned or echoed. Defaults to false (echo)&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Other ways to integrate==&lt;br /&gt;
&lt;br /&gt;
If it&#039;s undesirable to add a form, there are a couple of things you can do. For an example, see the export tab of the &#039;data&#039; module, which has already an export form, but just adds an option to export to portfolio rather than file:&lt;br /&gt;
&lt;br /&gt;
        require_once($CFG-&amp;gt;libdir . &#039;/portfoliolib.php&#039;);&lt;br /&gt;
        if (has_capability(&#039;mod/data:exportallentries&#039;, get_context_instance(CONTEXT_MODULE, $this-&amp;gt;_cm-&amp;gt;id))) {&lt;br /&gt;
            if ($portfoliooptions = portfolio_instance_select(&lt;br /&gt;
                portfolio_instances(),&lt;br /&gt;
                call_user_func(array(&#039;data_portfolio_caller&#039;, &#039;supported_formats&#039;)),&lt;br /&gt;
                &#039;data_portfolio_caller&#039;, &#039;&#039;, true, true)) {&lt;br /&gt;
                $mform-&amp;gt;addElement(&#039;header&#039;, &#039;notice&#039;, get_string(&#039;portfolionotfile&#039;, &#039;data&#039;) . &#039;:&#039;);&lt;br /&gt;
                $portfoliooptions[0] = get_string(&#039;none&#039;);&lt;br /&gt;
                ksort($portfoliooptions);&lt;br /&gt;
                $mform-&amp;gt;addElement(&#039;select&#039;, &#039;portfolio&#039;, get_string(&#039;portfolio&#039;, &#039;portfolio&#039;), $portfoliooptions);&lt;br /&gt;
            }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
This code adds a select option to the existing export form containing the available portfolio instances.&lt;br /&gt;
&lt;br /&gt;
Then in the form handler:&lt;br /&gt;
&lt;br /&gt;
    if (array_key_exists(&#039;portfolio&#039;, $formdata) &amp;amp;&amp;amp; !empty($formdata[&#039;portfolio&#039;])) {&lt;br /&gt;
        // fake  portfolio callback stuff and redirect&lt;br /&gt;
        $formdata[&#039;id&#039;] = $cm-&amp;gt;id;&lt;br /&gt;
        $formdata[&#039;exporttype&#039;] = &#039;csv&#039;; // force for now&lt;br /&gt;
        $url = portfolio_fake_add_url($formdata[&#039;portfolio&#039;], &#039;data_portfolio_caller&#039;, &#039;/mod/data/lib.php&#039;, $formdata);&lt;br /&gt;
        redirect($url);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
The portfolio_fake_add_url function returns the url that you need to redirect to, that would normally be the result of portfolio_add_button form being submitted.&lt;br /&gt;
&lt;br /&gt;
==A few extra notes on the caller base class==&lt;br /&gt;
&lt;br /&gt;
===protected $course===&lt;br /&gt;
There is a protected member variable, $course, that subclasses can set (with $this-&amp;gt;set(&#039;course&#039;, $course); ).&lt;br /&gt;
&lt;br /&gt;
portfolio_add_button tries to look for a course object at the point that it&#039;s called, by doing a global $COURSE which is hackish but mostly works. This is also used to build the navigation during the export process.  If for some reason, your navigation doesn&#039;t include the current course, you can set it like this. You shouldn&#039;t need to in the majority of cases.&lt;br /&gt;
&lt;br /&gt;
===serialization===&lt;br /&gt;
The caller object is stored in the database in serialized form.  The portfolio code works around this by loading the class definitions and then serializing and unserializing the objects again.  However, if you&#039;re storing any real objects in your caller class, you will need to do this as well.  See the assignment implementation for how this done (using php5&#039;s __wakeup function):&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    public function __wakeup() {&lt;br /&gt;
        require_once($this-&amp;gt;assignmentfile);&lt;br /&gt;
        $this-&amp;gt;assignment = unserialize(serialize($this-&amp;gt;assignment));&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
===setting portfolio internal===&lt;br /&gt;
Sometimes during prepare_package, you need to call functions in the libraries to render content as HTML that would normally also call portfolio_add_button.  This will result in &#039;you already have an export active in this session&#039; error - to get around this, do something like&lt;br /&gt;
 &lt;br /&gt;
&lt;br /&gt;
    define(&#039;PORTFOLIO_INTERNAL&#039;, true);&lt;br /&gt;
    modulename_print_some_htmlcontent();&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development_talk:DB_layer_2.0_migration_docs&amp;diff=40197</id>
		<title>Development talk:DB layer 2.0 migration docs</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development_talk:DB_layer_2.0_migration_docs&amp;diff=40197"/>
		<updated>2008-07-22T03:26:25Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: /* Why / when do we &amp;#039;&amp;#039;&amp;#039;have&amp;#039;&amp;#039;&amp;#039; to use the params array in the &amp;quot;_sql&amp;quot; functions? */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* The developer reading this article MUST read XMLDB Documentation (at least Introduction + three first Developing section pages) otherwise he&#039;s going to ask plenty of questions :) It could be very good that the first line of this article be: &amp;quot;In order to understand this article you need to know how works the database abstraction layer in Moodle (previous to 2.0)&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
* API link is broken (=&amp;gt; http://php.moodle.org/ &amp;quot;Server not found&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
* We know that some changes need to be done on 2.0. But on what? &lt;br /&gt;
All these examples concerns the glossary module. It would be good to indicate it first. When I read it first, I didn&#039;t really know what was going to be explained in XMLDB changes. In fact all this section is about &amp;quot;Updating your upgrade.php file for Moodle 2.0&amp;quot;. Conclusion: we really need a clear first sentence for the all &amp;quot;XMLDB changes&amp;quot; section. So we know what we are going to read. Once I knew what is about, all was very clear.&lt;br /&gt;
&lt;br /&gt;
* If we don&#039;t make any change on DDL code we shouldn&#039;t make it as a section/block. The DDL section should be a note. If you remove this section don&#039;t forget to update: &amp;quot;Changes below are grouped into 3 main blocks&amp;quot; =&amp;gt; &amp;quot;Changes below are grouped into 2 main blocks&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* DML section:&lt;br /&gt;
this is about to change all DML code, in all Moodle files? I think it miss an introduction line as well.&lt;br /&gt;
&lt;br /&gt;
* Maybe you should use more the word: YOU should do that, YOU do this, YOU ... The developer will feel more guided.&lt;br /&gt;
&lt;br /&gt;
Note: I wrote this comment on first read.&lt;br /&gt;
&lt;br /&gt;
Note 2: I&#039;m new as dev in Moodle, and I&#039;ve just discover all the power of Moodle XMLDB reading. All the current XMLDB looks awesome, I don&#039;t even talk about the coming 2.0 :)&lt;br /&gt;
&lt;br /&gt;
[[User:jerome mouneyrac|jerome mouneyrac]] 22:22, 22 May 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
== random things ==&lt;br /&gt;
&lt;br /&gt;
1 I added a marker re add_index or somesuch, is it really renamed add_key or is that just a copy/paste error? If it is correct, needs changing to emphasise that the function has really changed.&lt;br /&gt;
&lt;br /&gt;
2 The section with nothing in it (DDL) should probably be removed :)&lt;br /&gt;
&lt;br /&gt;
3 &#039;Golden&#039; and &#039;iron&#039; makes no sense at all to me, nor did the explanation. Could we rename these in some way?&lt;br /&gt;
&lt;br /&gt;
4 The example using named params should probably be changed to use better names for the params eg fn, ln - if you&#039;re using &#039;param1&#039; and &#039;param2&#039; you should use use the ordered one.&lt;br /&gt;
&lt;br /&gt;
5 Some of the examples are unnecessarily wordy - for example, why put the array into a variable then call the function? It only has two elements, you can do it in the same line no problem.&lt;br /&gt;
:Wordiness in code samples is perfectly OK, Sam, but you&#039;re free to be more terse in your implementations ;) [[User:Nicolas Connault|Nicolas Connault]] 06:59, 3 June 2008 (CDT)&lt;br /&gt;
::Yes, but my point is that it makes it look bad for people who are having to change. It&#039;s like, OMG! this was one line before and now it&#039;s four! wtf I have to define a separate array every time I scratch my backside! why are you making my life a misery, you unfeeling b****rds!!! That kind of thing. :) I don&#039;t think it makes it easier to understand to show use of a mostly-unnecessary separate variable. [[User:sam marshall|sam marshall]] 06:24, 6 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
6 Wow this new system is a fantastic improvement (except for the new IN function which is kind of nasty)&lt;br /&gt;
&lt;br /&gt;
[[User:sam marshall|sam marshall]] 04:32, 3 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
== Why / when do we &#039;&#039;&#039;have&#039;&#039;&#039; to use the params array in the &amp;quot;_sql&amp;quot; functions? ==&lt;br /&gt;
&lt;br /&gt;
# I&#039;m not sure I&#039;m clear on what the advantages are in using the &amp;quot;params&amp;quot; array for the &amp;quot;*_sql&amp;quot; functions (e.g. get_records_sql). Also, the text says we &#039;&#039;&#039;must&#039;&#039;&#039; do it, but I see lots of examples in the already converted code where this hasn&#039;t been done. Is it really a &#039;&#039;&#039;must do&#039;&#039;&#039; or is it more of a &#039;&#039;&#039;can do&#039;&#039;&#039;?&lt;br /&gt;
:You must always use the params array WHEN you have params to pass ;) If the SQL doesn&#039;t contain dynamic parameters (subject to SQL injection), we don&#039;t need the params array. [[User:Nicolas Connault|Nicolas Connault]] 13:57, 21 July 2008 (CDT)&lt;br /&gt;
:Aha! So the purpose is to help make sure that dynamic data used in SQL searches is cleansed? Is that correct? [[User:Mike Churchward|Mike Churchward]] 21:31, 21 July 2008 (CDT)&lt;br /&gt;
::Yes, and to get rid of the troublesome stripslashes().&lt;br /&gt;
# The new recordset handling seems odd. We used to use the &amp;quot;rs_fetch_next_record($rs)&amp;quot; function, which as I understood it was optimized so that the entire db records contents weren&#039;t stored in the PHP array variable. Now we are replacing it with a &amp;quot;foreach ($rs as $record)&amp;quot;. Doesn&#039;t that mean that the entire data query results are stored in a PHP variable again?&lt;br /&gt;
:The new recordset uses a new feature of PHP5, the Iterator interface. Foreach can then be used on the recordset. (See the bottom of [http://phplens.com/adodb/code.initialization.html the adodb doc] for more info). [[User:Nicolas Connault|Nicolas Connault]] 13:57, 21 July 2008 (CDT)&lt;br /&gt;
:Hmmm... Looks like I need to spend some time learning PHP5&#039;s features. So, in PHP5, the &amp;quot;foreach&amp;quot; construct unlocks a classes&#039; interator functions if they exist? [[User:Mike Churchward|Mike Churchward]] 21:31, 21 July 2008 (CDT)&lt;br /&gt;
::Correct :) [[User:Nicolas Connault|Nicolas Connault]] 22:24, 21 July 2008 (CDT)&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development_talk:DB_layer_2.0_migration_docs&amp;diff=40196</id>
		<title>Development talk:DB layer 2.0 migration docs</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development_talk:DB_layer_2.0_migration_docs&amp;diff=40196"/>
		<updated>2008-07-22T03:24:53Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Answering Mike&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* The developer reading this article MUST read XMLDB Documentation (at least Introduction + three first Developing section pages) otherwise he&#039;s going to ask plenty of questions :) It could be very good that the first line of this article be: &amp;quot;In order to understand this article you need to know how works the database abstraction layer in Moodle (previous to 2.0)&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
* API link is broken (=&amp;gt; http://php.moodle.org/ &amp;quot;Server not found&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
* We know that some changes need to be done on 2.0. But on what? &lt;br /&gt;
All these examples concerns the glossary module. It would be good to indicate it first. When I read it first, I didn&#039;t really know what was going to be explained in XMLDB changes. In fact all this section is about &amp;quot;Updating your upgrade.php file for Moodle 2.0&amp;quot;. Conclusion: we really need a clear first sentence for the all &amp;quot;XMLDB changes&amp;quot; section. So we know what we are going to read. Once I knew what is about, all was very clear.&lt;br /&gt;
&lt;br /&gt;
* If we don&#039;t make any change on DDL code we shouldn&#039;t make it as a section/block. The DDL section should be a note. If you remove this section don&#039;t forget to update: &amp;quot;Changes below are grouped into 3 main blocks&amp;quot; =&amp;gt; &amp;quot;Changes below are grouped into 2 main blocks&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* DML section:&lt;br /&gt;
this is about to change all DML code, in all Moodle files? I think it miss an introduction line as well.&lt;br /&gt;
&lt;br /&gt;
* Maybe you should use more the word: YOU should do that, YOU do this, YOU ... The developer will feel more guided.&lt;br /&gt;
&lt;br /&gt;
Note: I wrote this comment on first read.&lt;br /&gt;
&lt;br /&gt;
Note 2: I&#039;m new as dev in Moodle, and I&#039;ve just discover all the power of Moodle XMLDB reading. All the current XMLDB looks awesome, I don&#039;t even talk about the coming 2.0 :)&lt;br /&gt;
&lt;br /&gt;
[[User:jerome mouneyrac|jerome mouneyrac]] 22:22, 22 May 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
== random things ==&lt;br /&gt;
&lt;br /&gt;
1 I added a marker re add_index or somesuch, is it really renamed add_key or is that just a copy/paste error? If it is correct, needs changing to emphasise that the function has really changed.&lt;br /&gt;
&lt;br /&gt;
2 The section with nothing in it (DDL) should probably be removed :)&lt;br /&gt;
&lt;br /&gt;
3 &#039;Golden&#039; and &#039;iron&#039; makes no sense at all to me, nor did the explanation. Could we rename these in some way?&lt;br /&gt;
&lt;br /&gt;
4 The example using named params should probably be changed to use better names for the params eg fn, ln - if you&#039;re using &#039;param1&#039; and &#039;param2&#039; you should use use the ordered one.&lt;br /&gt;
&lt;br /&gt;
5 Some of the examples are unnecessarily wordy - for example, why put the array into a variable then call the function? It only has two elements, you can do it in the same line no problem.&lt;br /&gt;
:Wordiness in code samples is perfectly OK, Sam, but you&#039;re free to be more terse in your implementations ;) [[User:Nicolas Connault|Nicolas Connault]] 06:59, 3 June 2008 (CDT)&lt;br /&gt;
::Yes, but my point is that it makes it look bad for people who are having to change. It&#039;s like, OMG! this was one line before and now it&#039;s four! wtf I have to define a separate array every time I scratch my backside! why are you making my life a misery, you unfeeling b****rds!!! That kind of thing. :) I don&#039;t think it makes it easier to understand to show use of a mostly-unnecessary separate variable. [[User:sam marshall|sam marshall]] 06:24, 6 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
6 Wow this new system is a fantastic improvement (except for the new IN function which is kind of nasty)&lt;br /&gt;
&lt;br /&gt;
[[User:sam marshall|sam marshall]] 04:32, 3 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
== Why / when do we &#039;&#039;&#039;have&#039;&#039;&#039; to use the params array in the &amp;quot;_sql&amp;quot; functions? ==&lt;br /&gt;
&lt;br /&gt;
# I&#039;m not sure I&#039;m clear on what the advantages are in using the &amp;quot;params&amp;quot; array for the &amp;quot;*_sql&amp;quot; functions (e.g. get_records_sql). Also, the text says we &#039;&#039;&#039;must&#039;&#039;&#039; do it, but I see lots of examples in the already converted code where this hasn&#039;t been done. Is it really a &#039;&#039;&#039;must do&#039;&#039;&#039; or is it more of a &#039;&#039;&#039;can do&#039;&#039;&#039;?&lt;br /&gt;
:You must always use the params array WHEN you have params to pass ;) If the SQL doesn&#039;t contain dynamic parameters (subject to SQL injection), we don&#039;t need the params array. [[User:Nicolas Connault|Nicolas Connault]] 13:57, 21 July 2008 (CDT)&lt;br /&gt;
:Aha! So the purpose is to help make sure that dynamic data used in SQL searches is cleansed? Is that correct? [[User:Mike Churchward|Mike Churchward]] 21:31, 21 July 2008 (CDT)&lt;br /&gt;
# The new recordset handling seems odd. We used to use the &amp;quot;rs_fetch_next_record($rs)&amp;quot; function, which as I understood it was optimized so that the entire db records contents weren&#039;t stored in the PHP array variable. Now we are replacing it with a &amp;quot;foreach ($rs as $record)&amp;quot;. Doesn&#039;t that mean that the entire data query results are stored in a PHP variable again?&lt;br /&gt;
:The new recordset uses a new feature of PHP5, the Iterator interface. Foreach can then be used on the recordset. (See the bottom of [http://phplens.com/adodb/code.initialization.html the adodb doc] for more info). [[User:Nicolas Connault|Nicolas Connault]] 13:57, 21 July 2008 (CDT)&lt;br /&gt;
:Hmmm... Looks like I need to spend some time learning PHP5&#039;s features. So, in PHP5, the &amp;quot;foreach&amp;quot; construct unlocks a classes&#039; interator functions if they exist? [[User:Mike Churchward|Mike Churchward]] 21:31, 21 July 2008 (CDT)&lt;br /&gt;
::Correct on both counts :) [[User:Nicolas Connault|Nicolas Connault]] 22:24, 21 July 2008 (CDT)&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development_talk:DB_layer_2.0_migration_docs&amp;diff=40177</id>
		<title>Development talk:DB layer 2.0 migration docs</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development_talk:DB_layer_2.0_migration_docs&amp;diff=40177"/>
		<updated>2008-07-21T18:57:20Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Answers to Sam&amp;#039;s pertinent questions&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;* The developer reading this article MUST read XMLDB Documentation (at least Introduction + three first Developing section pages) otherwise he&#039;s going to ask plenty of questions :) It could be very good that the first line of this article be: &amp;quot;In order to understand this article you need to know how works the database abstraction layer in Moodle (previous to 2.0)&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
* API link is broken (=&amp;gt; http://php.moodle.org/ &amp;quot;Server not found&amp;quot;)&lt;br /&gt;
&lt;br /&gt;
* We know that some changes need to be done on 2.0. But on what? &lt;br /&gt;
All these examples concerns the glossary module. It would be good to indicate it first. When I read it first, I didn&#039;t really know what was going to be explained in XMLDB changes. In fact all this section is about &amp;quot;Updating your upgrade.php file for Moodle 2.0&amp;quot;. Conclusion: we really need a clear first sentence for the all &amp;quot;XMLDB changes&amp;quot; section. So we know what we are going to read. Once I knew what is about, all was very clear.&lt;br /&gt;
&lt;br /&gt;
* If we don&#039;t make any change on DDL code we shouldn&#039;t make it as a section/block. The DDL section should be a note. If you remove this section don&#039;t forget to update: &amp;quot;Changes below are grouped into 3 main blocks&amp;quot; =&amp;gt; &amp;quot;Changes below are grouped into 2 main blocks&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* DML section:&lt;br /&gt;
this is about to change all DML code, in all Moodle files? I think it miss an introduction line as well.&lt;br /&gt;
&lt;br /&gt;
* Maybe you should use more the word: YOU should do that, YOU do this, YOU ... The developer will feel more guided.&lt;br /&gt;
&lt;br /&gt;
Note: I wrote this comment on first read.&lt;br /&gt;
&lt;br /&gt;
Note 2: I&#039;m new as dev in Moodle, and I&#039;ve just discover all the power of Moodle XMLDB reading. All the current XMLDB looks awesome, I don&#039;t even talk about the coming 2.0 :)&lt;br /&gt;
&lt;br /&gt;
[[User:jerome mouneyrac|jerome mouneyrac]] 22:22, 22 May 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
== random things ==&lt;br /&gt;
&lt;br /&gt;
1 I added a marker re add_index or somesuch, is it really renamed add_key or is that just a copy/paste error? If it is correct, needs changing to emphasise that the function has really changed.&lt;br /&gt;
&lt;br /&gt;
2 The section with nothing in it (DDL) should probably be removed :)&lt;br /&gt;
&lt;br /&gt;
3 &#039;Golden&#039; and &#039;iron&#039; makes no sense at all to me, nor did the explanation. Could we rename these in some way?&lt;br /&gt;
&lt;br /&gt;
4 The example using named params should probably be changed to use better names for the params eg fn, ln - if you&#039;re using &#039;param1&#039; and &#039;param2&#039; you should use use the ordered one.&lt;br /&gt;
&lt;br /&gt;
5 Some of the examples are unnecessarily wordy - for example, why put the array into a variable then call the function? It only has two elements, you can do it in the same line no problem.&lt;br /&gt;
:Wordiness in code samples is perfectly OK, Sam, but you&#039;re free to be more terse in your implementations ;) [[User:Nicolas Connault|Nicolas Connault]] 06:59, 3 June 2008 (CDT)&lt;br /&gt;
::Yes, but my point is that it makes it look bad for people who are having to change. It&#039;s like, OMG! this was one line before and now it&#039;s four! wtf I have to define a separate array every time I scratch my backside! why are you making my life a misery, you unfeeling b****rds!!! That kind of thing. :) I don&#039;t think it makes it easier to understand to show use of a mostly-unnecessary separate variable. [[User:sam marshall|sam marshall]] 06:24, 6 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
6 Wow this new system is a fantastic improvement (except for the new IN function which is kind of nasty)&lt;br /&gt;
&lt;br /&gt;
[[User:sam marshall|sam marshall]] 04:32, 3 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
== Why / when do we &#039;&#039;&#039;have&#039;&#039;&#039; to use the params array in the &amp;quot;_sql&amp;quot; functions? ==&lt;br /&gt;
&lt;br /&gt;
# I&#039;m not sure I&#039;m clear on what the advantages are in using the &amp;quot;params&amp;quot; array for the &amp;quot;*_sql&amp;quot; functions (e.g. get_records_sql). Also, the text says we &#039;&#039;&#039;must&#039;&#039;&#039; do it, but I see lots of examples in the already converted code where this hasn&#039;t been done. Is it really a &#039;&#039;&#039;must do&#039;&#039;&#039; or is it more of a &#039;&#039;&#039;can do&#039;&#039;&#039;?&lt;br /&gt;
:You must always use the params array WHEN you have params to pass ;) If the SQL doesn&#039;t contain dynamic parameters (subject to SQL injection), we don&#039;t need the params array. [[User:Nicolas Connault|Nicolas Connault]] 13:57, 21 July 2008 (CDT)&lt;br /&gt;
# The new recordset handling seems odd. We used to use the &amp;quot;rs_fetch_next_record($rs)&amp;quot; function, which as I understood it was optimized so that the entire db records contents weren&#039;t stored in the PHP array variable. Now we are replacing it with a &amp;quot;foreach ($rs as $record)&amp;quot;. Doesn&#039;t that mean that the entire data query results are stored in a PHP variable again?&lt;br /&gt;
:The new recordset uses a new feature of PHP5, the Iterator interface. Foreach can then be used on the recordset. (See the bottom of [http://phplens.com/adodb/code.initialization.html the adodb doc] for more info). [[User:Nicolas Connault|Nicolas Connault]] 13:57, 21 July 2008 (CDT)&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Unit_tests&amp;diff=39181</id>
		<title>Development:Unit tests</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Unit_tests&amp;diff=39181"/>
		<updated>2008-07-04T12:40:03Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Instructions for Unit Testing in 2.0&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Moodle 1.7}}Location: &#039;&#039;Administration &amp;gt; Reports &amp;gt; Unit tests&#039;&#039;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The purpose of Unit Tests is to evaluate the individual parts of a program (functions, and methods of classes) to make sure that each element individually does the right thing. Unit Tests can be one of the first steps in a quality control process for developing or tweaking Moodle code.  The next steps will involve other forms of testing to ensure that these different parts work together properly. &lt;br /&gt;
&lt;br /&gt;
The unit testing framework is based on the [http://www.lastcraft.com/simple_test.php SimpleTest] framework. It was incorporated into Moodle by Nick Freear and Tim Hunt from [http://www.open.ac.uk/ The Open University].&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Running the unit tests in Moodle ==&lt;br /&gt;
&lt;br /&gt;
=== Running the basic tests ===&lt;br /&gt;
&lt;br /&gt;
# Log in with an admin account. &lt;br /&gt;
# Go to the admin screen.&lt;br /&gt;
# Click on the &#039;&#039;&#039;Reports&#039;&#039;&#039; link near the bottom of the page.&lt;br /&gt;
# Click on the &#039;&#039;&#039;Run the unit tests&#039;&#039;&#039; link.&lt;br /&gt;
# Wait for the tests to run.&lt;br /&gt;
&lt;br /&gt;
=== Options for running the tests ===&lt;br /&gt;
&lt;br /&gt;
At the bottom of the tests page, there is form that lets you adjust the options used when running the tests.&lt;br /&gt;
&lt;br /&gt;
==== Show passes as well as fails ====&lt;br /&gt;
&lt;br /&gt;
Normally, only details of the tests that have failed are printed. Turning on this options shows details of all the passes too.&lt;br /&gt;
&lt;br /&gt;
==== Show the search for test files ====&lt;br /&gt;
&lt;br /&gt;
The tests to run are found automatically be searching the codebase for files whose names match &#039;&#039;&#039;test*.php&#039;&#039;&#039; in directories called &#039;&#039;&#039;simpletest&#039;&#039;&#039;. Turning on this option will print a list of the folders searched and the test files found. This is sometimes useful for debugging.&lt;br /&gt;
&lt;br /&gt;
This option is particularly useful when one of your test files has a syntax error. When this happens, you sometimes just get a blank page with no error message. Turning on the show search option lets you see which test file it was that gave the error. If necessary, you can enable this option manually by adding &amp;quot;showsearch=1&amp;quot; to the end of the URL.&lt;br /&gt;
&lt;br /&gt;
==== Run a thorough test (may be slow) ====&lt;br /&gt;
&lt;br /&gt;
If you turn on this option, then as well as looking for files called &#039;&#039;&#039;test*.php&#039;&#039;&#039;, the search also looks for files called &#039;&#039;&#039;slowtest*.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
To be useful, the full test run should find most bugs, but not take too long to complete. So if you have very, very detailed tests of an area of the code, it may be better to select a subset for everday testing, and only use the more detailed tests when a bug is reported, or you are doing new development in that area of the code.&lt;br /&gt;
&lt;br /&gt;
This option is most useful when combined with the next option.&lt;br /&gt;
&lt;br /&gt;
==== Only run tests in ====&lt;br /&gt;
&lt;br /&gt;
Normally, tests from all parts of the codebase are run. However, when you are just doing development of one part of the code, that is a waste of time. You can type the name of a folder (for example &#039;&#039;&#039;mod/quiz&#039;&#039;&#039;) or a particular test file (for example &#039;&#039;&#039;lib/simpletest/testdatalib.php&#039;&#039;&#039;) and then only those tests will be run.&lt;br /&gt;
&lt;br /&gt;
[[Image:RunOnlyTheseTests.png|right]] Instead of typing a path into this box, there is an easier way. Whenever a pass or fail is displayed, the name of the test file is printed. Each section of the path name is a link to run only the tests in that folder or file.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Writing new tests ==&lt;br /&gt;
&lt;br /&gt;
As an example, suppose we wanted to start writing tests for the functions in the file &#039;question/editlib.php&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Where to put the tests ===&lt;br /&gt;
&lt;br /&gt;
If you have read the first half of this page and were paying attention, you can probably work out that you should create a folder called &#039;&#039;&#039;question/testeditlib&#039;&#039;&#039;, and create a file in there called something like &#039;&#039;&#039;testeditlib.php&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
The skeleton of this file should look like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&amp;lt;?php&lt;br /&gt;
/**&lt;br /&gt;
 * Unit tests for (some of) question/editlib.php.&lt;br /&gt;
 *&lt;br /&gt;
 * @copyright &amp;amp;copy; 2006 The Open University&lt;br /&gt;
 * @author T.J.Hunt@open.ac.uk&lt;br /&gt;
 * @license http://www.gnu.org/copyleft/gpl.html GNU Public License&lt;br /&gt;
 * @package question&lt;br /&gt;
 */&lt;br /&gt;
&lt;br /&gt;
/** */&lt;br /&gt;
require_once(dirname(__FILE__) . &#039;/../../config.php&#039;);&lt;br /&gt;
&lt;br /&gt;
global $CFG;&lt;br /&gt;
require_once($CFG-&amp;gt;libdir . &#039;/simpletestlib.php&#039;); // Include the test libraries&lt;br /&gt;
require_once($CFG-&amp;gt;dirroot . &#039;/question/editlib.php&#039;); // Include the code to test&lt;br /&gt;
&lt;br /&gt;
/** This class contains the test cases for the functions in editlib.php. */&lt;br /&gt;
class question_editlib_test extends UnitTestCase {&lt;br /&gt;
    function test_get_default_question_category() {&lt;br /&gt;
        // Do the test here/&lt;br /&gt;
    }&lt;br /&gt;
}&lt;br /&gt;
?&amp;gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
That is, you have a class called something_test, and in that class you have lots of methods called test_something. Normally, you have one test method for each function you want to test, and you may as well called the test method &#039;&#039;&#039;test_name_of_function_being_tested&#039;&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
=== Inside a test function ===&lt;br /&gt;
&lt;br /&gt;
The inside of a test function tyically looks like this: &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;function test_get_default_question_category() {&lt;br /&gt;
    // Set things up in preparation for the test.&lt;br /&gt;
&lt;br /&gt;
    // Call the function you want to test.&lt;br /&gt;
&lt;br /&gt;
    // Check that the result is what you expected.&lt;br /&gt;
}&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
For example:&lt;br /&gt;
&lt;br /&gt;
=== Test data ===&lt;br /&gt;
&lt;br /&gt;
TODO&lt;br /&gt;
&lt;br /&gt;
=== setUp and tearDown methods ===&lt;br /&gt;
&lt;br /&gt;
If all your test cases relate to the same area of code, and so need the same set of test data, then you can create a method called &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; that sets up the test data. If present, this method will be called before each test method. You can write a matching &amp;lt;code&amp;gt;tearDown()&amp;lt;/code&amp;gt; method if there is any clean-up that needs to be done after each test case has run.&lt;br /&gt;
&lt;br /&gt;
If you have some test test cases the need one sort of setup, and some other test cases that need a different setup, consider splitting your tests into two separate classes, each with its own &amp;lt;code&amp;gt;setUp()&amp;lt;/code&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
=== Further information ===&lt;br /&gt;
&lt;br /&gt;
The simpletest documentation is at: http://simpletest.sourceforge.net/.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Changes to your existing code to make it work with unit testing ==&lt;br /&gt;
&lt;br /&gt;
When code is being tested, it gets included from inside one of the simpletest library function. If the code is expecting to be run directly (for example, if it is a view.php or index.php function), you are likely to get errors because that expectation is no longer true.&lt;br /&gt;
&lt;br /&gt;
=== Include paths ===&lt;br /&gt;
&lt;br /&gt;
Includes like&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;require_once(&#039;../../config.php&#039;); // Won&#039;t work.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
won&#039;t work. Instead, the more robust option is &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;require_once(dirname(__FILE__) . &#039;/../../config.php&#039;); // Do this.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Access to global variables ===&lt;br /&gt;
&lt;br /&gt;
Because your code was included from within a function, you can&#039;t access global variables until you have done a global statement.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;require_once(dirname(__FILE__) . &#039;/../../config.php&#039;);&lt;br /&gt;
require_once($CFG-&amp;gt;libdir . &#039;/moodlelib.php&#039;); // Won&#039;t work.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;require_once(dirname(__FILE__) . &#039;/../../config.php&#039;);&lt;br /&gt;
&lt;br /&gt;
global $CFG; // You need this.&lt;br /&gt;
require_once($CFG-&amp;gt;libdir . &#039;/moodlelib.php&#039;); // Will work now.&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Unit testing in 2.0 ==&lt;br /&gt;
{{Moodle 2.0}}&lt;br /&gt;
With the Objectification of the Database libraries in Moodle 2.0, new and better approaches to Unit testing can be used. Here is a sample of a simple test case: (in course/simpletest)&lt;br /&gt;
&lt;br /&gt;
 require_once($CFG-&amp;gt;dirroot . &#039;/course/lib.php&#039;);&lt;br /&gt;
 &lt;br /&gt;
 global $DB;&lt;br /&gt;
 Mock::generate(get_class($DB), &#039;mockDB&#039;);&lt;br /&gt;
 &lt;br /&gt;
 class courselib_test extends UnitTestCase {&lt;br /&gt;
     var $realDB;&lt;br /&gt;
 &lt;br /&gt;
     function setUp() {&lt;br /&gt;
         global $DB;&lt;br /&gt;
         $this-&amp;gt;realDB = clone($DB);&lt;br /&gt;
         $DB = new mockDB();&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     function tearDown() {&lt;br /&gt;
         global $DB;&lt;br /&gt;
         $DB = $this-&amp;gt;realDB;&lt;br /&gt;
     }&lt;br /&gt;
 &lt;br /&gt;
     function testMoveSection() {&lt;br /&gt;
         global $DB;&lt;br /&gt;
         $course = new stdClass();&lt;br /&gt;
         $course-&amp;gt;id = 1;&lt;br /&gt;
 &lt;br /&gt;
         $sections = array();&lt;br /&gt;
         for ($i = 1; $i &amp;lt; 11; $i++) {&lt;br /&gt;
             $sections[$i] = new stdClass();&lt;br /&gt;
             $sections[$i]-&amp;gt;id = $i;&lt;br /&gt;
             $sections[$i]-&amp;gt;section = $i - 1;&lt;br /&gt;
         }&lt;br /&gt;
 &lt;br /&gt;
         $DB-&amp;gt;expectOnce(&#039;get_records&#039;, array(&#039;course_sections&#039;, array(&#039;course&#039; =&amp;gt; $course-&amp;gt;id)));&lt;br /&gt;
         $DB-&amp;gt;setReturnValue(&#039;get_records&#039;, $sections);&lt;br /&gt;
         $this-&amp;gt;assertFalse(move_section($course, 2, 3));&lt;br /&gt;
     }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Further reading about unit testing ==&lt;br /&gt;
&lt;br /&gt;
The best book I know about unit testing is &#039;&#039;&#039;Pragmatic Unit Testing in Java with JUnit&#039;&#039; by Andrew Hunt (no relation) and David Thomas. I know, this book is not called Pragmatic Unit Testing in PHP with SimpleTest. However, it is an excellent book - short, to the point, and very practical. Most of what it says is not specific to Java and JUnit and it is obvious how to apply it in our testing setup.&lt;br /&gt;
&lt;br /&gt;
[[Category:Developer|Unit tests]]&lt;br /&gt;
[[Category:Report]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:Repository_File_Picker&amp;diff=39171</id>
		<title>Development:Repository File Picker</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:Repository_File_Picker&amp;diff=39171"/>
		<updated>2008-07-04T06:23:57Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: fixing typos&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&#039;&#039;&#039;Functional Specification Revisions:&#039;&#039;&#039; &lt;br /&gt;
:0.1 - 30/06/2008 - Jerome Mouneyrac - Draft Version&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Audience:&#039;&#039;&#039; &lt;br /&gt;
:Developer/QA tester&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Status:&#039;&#039;&#039; &lt;br /&gt;
:not implemented ([http://tracker.moodle.org/browse/MDL-15348 tracker issue])&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;Related Documents:&#039;&#039;&#039;&lt;br /&gt;
*[[Development:Repository API| Repository API]]&lt;br /&gt;
*[[Development:Repository_Plugins| Repository Plugins]]&lt;br /&gt;
*[[Development:Repository_Interface_for_Moodle/Course/User| Repository Interface for Moodle/Course/User]]&lt;br /&gt;
*[[QA:Use Case Number Attribution| Use Case Number Attribution]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&lt;br /&gt;
==Introduction==&lt;br /&gt;
This document is about functional specification for the file picker&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Requirements==&lt;br /&gt;
* be able to select a file in a external repository (in order to be associated with an activity, resource, user profil,...)&lt;br /&gt;
&lt;br /&gt;
==User Interface==&lt;br /&gt;
&lt;br /&gt;
=== Javascript enabled ===&lt;br /&gt;
&lt;br /&gt;
==== Moodleform ====&lt;br /&gt;
Following a picture of the Moodleform user interface: one button to display or undisplay the file picker, the read-only filename text field, and the ajax file picker.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Image:Ajax_file_picker.png]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== HTML editor ====&lt;br /&gt;
Following a mock screen of the HTML editor:&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
[[Image:Htmleditor.png]]&lt;br /&gt;
&amp;lt;br /&amp;gt;&amp;lt;br /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Javascript disabled ===&lt;br /&gt;
As there is no HTML editor when javascript is disabled, we look to the Moodleform case only:&amp;lt;br /&amp;gt;&lt;br /&gt;
on every page when a user wishes to add a file, he clicks on the Add file button. A new page with the file picker is displayed in the same window. The file picker user interface should be similar to the one with Javascript, except that every user action will refresh the entire page. Once the user clicks on Select button, he&#039;s redirected to the previous page. The previously entered data (by the user) are still displayed.&lt;br /&gt;
&lt;br /&gt;
===Mock file picker screenshots===&lt;br /&gt;
When you first call up the file picker and choose a repository, you might be asked to log in (if saving of passwords is not allowed):&lt;br /&gt;
&lt;br /&gt;
[[Image:Filepicker_login.jpg]]&lt;br /&gt;
&lt;br /&gt;
Browsing files could look something like this:&lt;br /&gt;
&lt;br /&gt;
[[Image:Filepicker_browser.jpg]]&lt;br /&gt;
&lt;br /&gt;
And you can also search:&lt;br /&gt;
&lt;br /&gt;
[[Image:Filepicker_search.jpg]]&lt;br /&gt;
&lt;br /&gt;
==Use Cases==&lt;br /&gt;
TODO: please write them&lt;br /&gt;
===UC004-1 Select a file from an Moodleform===&lt;br /&gt;
====Base scenario====&lt;br /&gt;
#...&lt;br /&gt;
&lt;br /&gt;
====Pre conditions====&lt;br /&gt;
:- repository plugin is enabled and has been setup in the administration&lt;br /&gt;
&lt;br /&gt;
===UC004-2 Select a file from HTML editor===&lt;br /&gt;
===UC004-3 Select a file when javascript is disabled===&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:File_API&amp;diff=39147</id>
		<title>Development:File API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:File_API&amp;diff=39147"/>
		<updated>2008-07-03T15:04:37Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: Comment on filesize calculation&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page outlines the current thinking about implementing file storage and access in Moodle 2.0.   It&#039;s a SPECIFICATION UNDER CONSTRUCTION!&lt;br /&gt;
&lt;br /&gt;
The page is open for everyone so everyone can help correct mistakes and help with the evolution of this document.  However, if you have questions, problems to report or major changes to suggest please add them to the [[Development_talk:File_API|page comments]], or start a discussion in the [http://moodle.org/mod/forum/view.php?id=1807 Repositories forum].  We&#039;ll endeavour to merge all such suggestions into the main spec before we start development.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objectives==&lt;br /&gt;
&lt;br /&gt;
# Allow files to be added directly into Moodle (as we do now)&lt;br /&gt;
# Remember where files came from&lt;br /&gt;
# Give modules control over the access to files using capabilities and other local rules&lt;br /&gt;
# Consistent and simple approach for ALL file handling throughout Moodle&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The File API is a core set of interfaces that all Moodle code will use to:&lt;br /&gt;
# store files within Moodle&lt;br /&gt;
# display files to Moodle users&lt;br /&gt;
&lt;br /&gt;
It applies only to &amp;quot;user&amp;quot; files.  It will NOT apply to local files and caches created by Moodle such as these directories in dataroot: temp, lang, cache, environment, filter, rss, search, sessions, upgradelogs etc&lt;br /&gt;
&lt;br /&gt;
The API will be split into several independent parts:&lt;br /&gt;
# File serving API&lt;br /&gt;
## file.php&lt;br /&gt;
## pluginfile.php&lt;br /&gt;
## userfile.php&lt;br /&gt;
## rssfile.php&lt;br /&gt;
# File storage API&lt;br /&gt;
## optional access control&lt;br /&gt;
## optional repo sync&lt;br /&gt;
# File management API&lt;br /&gt;
## File browsing&lt;br /&gt;
## File linking (editor integration)&lt;br /&gt;
## Upload from repository&lt;br /&gt;
&lt;br /&gt;
==File serving API==&lt;br /&gt;
Deals with serving of files - browser requests file, Moodle sends it back. We have three main files. It is important to setup slasharguments on server (file.php/some/thing/xxx.jpg), any content that relies on relative links can not work without it (scorm, uploaded html pages, etc.).&lt;br /&gt;
&lt;br /&gt;
===file.php===&lt;br /&gt;
Serves course files.&lt;br /&gt;
&lt;br /&gt;
Implements basic file access. Ideally only images and files linked from course sections should be there, no XSS protection required - we expect javascript, sw, etc. there, no way to make it &amp;quot;secure&amp;quot;. The access control is not critical any more if we move most of the files into modules&lt;br /&gt;
&lt;br /&gt;
The file name and parameter structure is critical for backwards compatibility of existing course content.&lt;br /&gt;
&lt;br /&gt;
 /file.php/courseid/dir/dir/filename.ext&lt;br /&gt;
&lt;br /&gt;
Internally the files would be stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;$coursecontextid, &#039;filearea&#039;=&amp;gt;&#039;content&#039;, &#039;itemid&#039;=&amp;gt;0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===pluginfile.php===&lt;br /&gt;
(aka modfile.php)&lt;br /&gt;
Sends module, block, question files.&lt;br /&gt;
* modules decide about access control&lt;br /&gt;
* optional XSS protection - student submitted files must not be served with normal headers, we have to force download instead; ideally there should be second wwwroot for serving of untrusted files&lt;br /&gt;
* only internal links to selected areas are supported - you can link images in summary area, but not the assignment submissions&lt;br /&gt;
&lt;br /&gt;
Absolute file links need to be rewritten if html editing allowed in module. The links are stored internally as relative links. Before editing or display the internal link representation is converted to absolute links using simple str_replace() @@thipluginlink/summary@@/image.jpg --&amp;gt; /pluginfile.php/assignmentcontextid/summary/image.jpg, it is converted back to internal links before saving.&lt;br /&gt;
&lt;br /&gt;
::Can the distinct file areas supported by one plugin be declared somehow in order add some information about them? For example, I think it can be interesting to declare:&lt;br /&gt;
::* assignment_summary:&lt;br /&gt;
::** relpath=&#039;summary&#039;&lt;br /&gt;
::** userdata=false&lt;br /&gt;
::** anotherproperty=anothervalue&lt;br /&gt;
::* assignment_submission:&lt;br /&gt;
::** relpath=&#039;submission/@@USERID@@&#039;&lt;br /&gt;
::** userdata=false&lt;br /&gt;
::** anotherproperty=anothervalue&lt;br /&gt;
::* and so on...&lt;br /&gt;
::And then, when the editor &amp;quot;receives&amp;quot; one &amp;quot;assignment_summary&amp;quot; areaname, if knows what to show and so on? Also that info could be useful to know, in backup &amp;amp; restore if some fileareas have to be processed or no (userdata=false). Or also, when reconstructing the links (str_replace() above). And will cause to have a well defined list of fileareas by module, instead of coding them in a free way (prone to errors). [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 16:35, 28 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
::Something like this will be part of file management API, hardcoding this in file storage would make it less flexible imo [[User:Skodak|Skodak]]&lt;br /&gt;
&lt;br /&gt;
::Yup, yup. Storage doesn&#039;t know anything but get/put files (nothing else). It&#039;s part of management, absolutely. [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 11:21, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/contextid/areaname/arbitrary/params/or/dirs/filename.ext&lt;br /&gt;
&lt;br /&gt;
pluginfile.php detects the type of plugin from context table, fetches basic info (like $course or $cm if appropriate) and calls plugin function (or later method) which does the access control and finally sends the file to user. &#039;&#039;areaname&#039;&#039; separates files by type and divides the context into several subtrees - for example &#039;&#039;summary&#039;&#039; files (images used in module intros), post attachments, etc.&lt;br /&gt;
&lt;br /&gt;
====assignment example====&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/summary/someimage.jpg&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/submission/submissionid/attachmentname.ext&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/extra/allsubmissionfiles.zip&lt;br /&gt;
&lt;br /&gt;
::Uhm... all those files together? What&#039;s going to differentiate the &amp;quot;submission&amp;quot; path in the example above from the &amp;quot;summary&amp;quot; path? Is it supposed that the editor, or the filemanager won&#039;t allow , for example to pick-up one file from the &amp;quot;submission&amp;quot; area to be used in the summary of one assignment and only the &amp;quot;summary&amp;quot; area will be showed? That means multiple file managers by context and it&#039;s against the clean &amp;quot;one file manager per context&amp;quot; agreed below [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 21:28, 26 June 2008 (CDT)&lt;br /&gt;
::Yes Eloy, the different areas (summary, submission) etc. have different uses, different access control. There are two types of file manager - the two pane file manager which lists all contexts+areas user may access, and minimalistic manager in html editor which shows only subset of areas from current plugin (because you can not link anything else).&lt;br /&gt;
&lt;br /&gt;
====scorm example====&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/scormcontextid/summary/someimage.jpg&lt;br /&gt;
 /pluginfile.php/scormcontextid/content/revisionnumber/dir/somescormfile.js&lt;br /&gt;
&lt;br /&gt;
The revision counter is incremented when any file changes in order to prevent caching problems. The lifetime should be adjustable in module settings.&lt;br /&gt;
&lt;br /&gt;
====quiz example====&lt;br /&gt;
&lt;br /&gt;
 pluginfile.php/quizcontextid/summary/niceimage.jpg&lt;br /&gt;
 pluginfile.php/quizcontextid/report/type/export.ods&lt;br /&gt;
&lt;br /&gt;
====questions example====&lt;br /&gt;
&lt;br /&gt;
 pluginfile.php/SYSCONTEXTID/question/questionid/file.jpg&lt;br /&gt;
&lt;br /&gt;
====blog example====&lt;br /&gt;
Blog entries or notes in general do not have context id (because they live in system context, SYSCONTEXTID below is the id of system context).&lt;br /&gt;
The note attachments are always served with XSS protection on, ideally we should use separate wwwroot for this. Access control can be hardcoded.&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/SYSCONTEXTID/blog/blogentryid/attachmentname.ext&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;SYSCONTEXTID, &#039;filearea&#039;=&amp;gt;&#039;blog&#039;, &#039;itemid&#039;=&amp;gt;$blogentryid)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====backup example====&lt;br /&gt;
It would be nice to have some special protection of backup files - new capabilities for backup file download, upload. Backups contain a lot of personal info, we could block restoring of backups from other sites too.&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/coursecontextid/backup/backupfile.zip&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;$coursecontextid, &#039;filearea&#039;=&amp;gt;&#039;backup&#039;, &#039;itemid&#039;=&amp;gt;0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===userfile.php===&lt;br /&gt;
Personal file storage, intended as an online storage of work in progress like assignments before the submission.&lt;br /&gt;
* read/write own files only for now&lt;br /&gt;
* option to share with others later&lt;br /&gt;
* personal &amp;quot;websites&amp;quot; will not be supported (security)&lt;br /&gt;
&lt;br /&gt;
 /userfile.php/userid/dir/dir/filename.ext&lt;br /&gt;
&lt;br /&gt;
===rssfile.php===&lt;br /&gt;
Replaces rss/file.php which is kept only for backwards compatibility.&lt;br /&gt;
RSS files should not require sessions/cookies, URLs should contain some sort of security token/key.&lt;br /&gt;
Internally the files may be stored in database or together with other files.&lt;br /&gt;
Performance improvements - we should support both Etag (cool) and Last-Modified (more used), when we receive If-None-Match/If-Modified-Since =&amp;gt; 304 &lt;br /&gt;
&lt;br /&gt;
 /rssfile.php/contextid/any/parameters/module/wants/rss.xml&lt;br /&gt;
 /rssfile.php/SYSCONTEXTID/blog/userid/rss.xml&lt;br /&gt;
&lt;br /&gt;
Again modules and plugins decide what gets sent to user.&lt;br /&gt;
&lt;br /&gt;
===Temporary files===&lt;br /&gt;
Temporary files are usually used during the lifetime of one script only.&lt;br /&gt;
uses:&lt;br /&gt;
* exports&lt;br /&gt;
* imports&lt;br /&gt;
* zipping/unzipping&lt;br /&gt;
* processing by executable files (latex, mimetex)&lt;br /&gt;
&lt;br /&gt;
Ideally these files should never use utf-8 (which is a major problem for zipping at the moment).&lt;br /&gt;
Proposed new sha1 based file storage is not suitable both for performance and technical reasons.&lt;br /&gt;
&lt;br /&gt;
===Legacy file storage and serving===&lt;br /&gt;
Going to use good-old separate directories in $CFG-&amp;gt;dataroot.&lt;br /&gt;
&lt;br /&gt;
file serving and storage:&lt;br /&gt;
# user avatars - user/pix.php&lt;br /&gt;
# group avatars - user/pixgroup.php&lt;br /&gt;
# tex, algebra - filter/tex/* and filter/algebra/*&lt;br /&gt;
# rss cache (?full rss rewrite soon?) - backwards compatibility only rss/file.php&lt;br /&gt;
&lt;br /&gt;
only storage:&lt;br /&gt;
#sessions&lt;br /&gt;
&lt;br /&gt;
==File storage API==&lt;br /&gt;
Modules in general work only with local Moodle files. One of the major reason is performance when accessing external repository files. It will be possible to use repositories instead of file uploading and also to keep local files synced with external repository.&lt;br /&gt;
&lt;br /&gt;
File contents are stored in moodledata/filepool indexed using SHA1 hashes instead of file names; file names, relative paths and other metadata will be stored in file(_xxx) database tables. This should be fully abstracted so that modules do not actually know where the files are located. When storing files the content is sent as string or file handle, when reading content it is returned as file handle.&lt;br /&gt;
&lt;br /&gt;
===files table===&lt;br /&gt;
&lt;br /&gt;
This table contains one entry for every file.  Enough information is kept here so that the file can be fully identified and retrieved again if necessary.&lt;br /&gt;
&lt;br /&gt;
note: plural used because file is a reserved word&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|sha1hash&lt;br /&gt;
|varchar(40)&lt;br /&gt;
| &lt;br /&gt;
|The sha1 hash of content.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;contextid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|The context id defined in context table - identifies the instance of plugin owning the file.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filearea&lt;br /&gt;
|varchar(50)&lt;br /&gt;
|&lt;br /&gt;
|Like &amp;quot;submissions&amp;quot;, &amp;quot;intro&amp;quot; and &amp;quot;content&amp;quot; (images and swf linked from summaries), etc.; &amp;quot;blogs&amp;quot; and &amp;quot;userfiles&amp;quot; are special case that live at the system context.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|itemid&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|Some plugin specific item id (eg. forum post, blog entry or assignment submission or user id for user files)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filepath&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|relative path to file from module content root, useful in Scorm and Resource mod - most of the mods do not need this&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filename&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The full Unicode name of this file (case sensitive)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filesize&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|size of file - bytes&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|mimetype&lt;br /&gt;
|varchar(100)&lt;br /&gt;
|NULL&lt;br /&gt;
|type of file&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;userid&#039;&#039;&#039;&lt;br /&gt;
|int(10)  &lt;br /&gt;
|NULL&lt;br /&gt;
|Optional - general user id field - meaning depending on plugin&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The time this file was created&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The last time the file was modified&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
index on &amp;quot;contextid, filearea, itemid&amp;quot; and &amp;quot;sha1hash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Plugin type is not specified because it is derived from contextid, items like blog that do not have own context will use own filearea usually from systemcontextid.&lt;br /&gt;
&lt;br /&gt;
::Perhpas we could also hash filepath and filename and index by them, to save some text limitations in the DB side (length limits of indexes, not indexable, complex retrieval...). [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 11:54, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
::Also, perhaps we should store finally the plugin type there to save some queries per request, using it to drive to the correct file handling of each plugin. [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 18:54, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
=== files_cleanup table ===&lt;br /&gt;
&lt;br /&gt;
This table contains candidates for deletion from the file pool. Files are not deleted immediately, cron uses the files_cleanup table, verifies the file is not used any more and deletes it from pool. Reasons for cron clean-up are performance and prevention of collision - there could be a problem with concurrent uploads and deletes, we will probably need to add some table-based locking during the clean-up.&lt;br /&gt;
&lt;br /&gt;
We might add an extra script that does deep validation of pool area - report missing files, report orphaned files, content not matching the sha1 filename, etc. - this would very very time consuming.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|sha1hash&lt;br /&gt;
|varchar(40)&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== files_metadata table ===&lt;br /&gt;
&lt;br /&gt;
This table contains extra metadata about files.  Repositories could provide this, or it could be manually edited in the local copy.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;fileid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|Id of file.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;name&#039;&#039;&#039;&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The name of extra metadata&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|value&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|Value&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===files_acl table===&lt;br /&gt;
&lt;br /&gt;
This table describes optional ACL for file. This is not required in majority of cases, modules usually hardcode the file access logic, course files should not be used much any more.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;fileid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
| &lt;br /&gt;
|The file we are defining access for&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;contextid&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The context where this file is being published&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;capability&#039;&#039;&#039;&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|The capability that is required to see this file.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====acl notes====&lt;br /&gt;
* this is missing some concept similar to &#039;&#039;&#039;user/group/others&#039;&#039;&#039;, for example in case of user files typical user can not assign permissions or view them - this becomes useless there&lt;br /&gt;
* it is more important to synchronise the availability of file link and the file itself - having link pointing to inaccessible file or file which is accessible when not wanted are both problems&lt;br /&gt;
* browser/proxy caching works against us here - &amp;quot;secret&amp;quot; files should not be cached&lt;br /&gt;
&lt;br /&gt;
===files_sync table===&lt;br /&gt;
&lt;br /&gt;
This table contains information on how to synchronise data with repositories. Data would be synchronised from cron.php or on demand from file manager. The sync would be one way only (repository--&amp;gt;local file).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;fileid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|Id of file.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;repositoryid&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The repository instance this is associated with, see [[Development:Repository_API]]&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|updates&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|Specifies the update schedule (0 = none, 1 = on demand, other = some period in seconds)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|repositorypath&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|The full path to the original file on the repository&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timeimportfirst&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The first time this file was imported into Moodle&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timeimportlast&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The most recent time that this file was imported into Moodle&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===File content storage===&lt;br /&gt;
Originally the file storage hierarchy contained a lot of metadata including userids, entry ids, filenames, etc. The file content will now be stored separately from file metadata. It must supports utf8 on all platforms.&lt;br /&gt;
&lt;br /&gt;
File storing:&lt;br /&gt;
# calculate SHA1 hash of content&lt;br /&gt;
# check if file with SHA1 name exists, if not add the file to file pool&lt;br /&gt;
# remove SHA1 from list of deleted files if found there&lt;br /&gt;
# store file in &#039;&#039;file&#039;&#039; table, use SHA1 as file pool identifier&lt;br /&gt;
&lt;br /&gt;
File reading:&lt;br /&gt;
#fetch file record from &#039;file&#039; table - probably using file id or combination of contextid+instanceid&lt;br /&gt;
#fetch content of file &lt;br /&gt;
&lt;br /&gt;
File deleting:&lt;br /&gt;
#delete record from &#039;&#039;file&#039;&#039; table, remember file SHA1&lt;br /&gt;
#store the deleted SHA1 in deleted files table, do not remove the physical file yet&lt;br /&gt;
#wait for cron cleanup script to actually delete the file named SHA1 (proper table locking needed to prevent race conditions when adding/deleting files)&lt;br /&gt;
&lt;br /&gt;
====File pool details====&lt;br /&gt;
located in $CFG-&amp;gt;dataroot/filepool/, all files can not be stored in one directory due to OS limitations, it uses 3 levels based on first three characters of sha1 hash. It is unlikely that there will be thousands of files with the same first 3 chars in sha1 hash of their content.&lt;br /&gt;
&lt;br /&gt;
This type of storage saves a lot of disk space when storing multiple copies of the same large file. It can also help substantially when synchronising data with external repositories. Another benefit is we can detect inconsistencies in file content.&lt;br /&gt;
&lt;br /&gt;
File read performance is similar to previous code, file write performance will be slower - due to hashing and extra database access.&lt;br /&gt;
&lt;br /&gt;
 dataroot &lt;br /&gt;
    /filepool&lt;br /&gt;
       /00&lt;br /&gt;
       /01&lt;br /&gt;
       ...&lt;br /&gt;
       /&#039;&#039;&#039;23&#039;&#039;&#039;&lt;br /&gt;
         /00&lt;br /&gt;
         /01&lt;br /&gt;
         ...&lt;br /&gt;
         /&#039;&#039;&#039;1e&#039;&#039;&#039;&lt;br /&gt;
            /00&lt;br /&gt;
            /01&lt;br /&gt;
            ...&lt;br /&gt;
            /&#039;&#039;&#039;2d&#039;&#039;&#039;&lt;br /&gt;
               /231e2dc421be4fcd0172e5afceea3970e2f3d940.jpg&lt;br /&gt;
       ...&lt;br /&gt;
       /fe&lt;br /&gt;
       /ff&lt;br /&gt;
&lt;br /&gt;
==File management API==&lt;br /&gt;
&lt;br /&gt;
This section describes following:&lt;br /&gt;
#file manager&lt;br /&gt;
#integration with html editor&lt;br /&gt;
#interactions with repos&lt;br /&gt;
&lt;br /&gt;
===File manager===&lt;br /&gt;
Single pane file manager is hard to implement without drag &amp;amp; drop which is notoriously problematic in web based applications. I propose to implement a two pane commander-style file manager. Two pane manager allows you to easily copy/move files between two different contexts (ex: courses).&lt;br /&gt;
&lt;br /&gt;
File manager must not interact directly with filesystem API, instead each module should return traversable tree of files and directories with both real and localised names (localised names are needed for dirs like backupdata).&lt;br /&gt;
&lt;br /&gt;
Originally there was a single file tree for each course. We need to fully separate each module/block from the course files and there might be also independent file areas in modules (ex: module introduction, content files, submissions, post attachments). File area may be defined as a small tree where we can use relative paths. These file areas are hanging from the branches of the context tree (this needs a picture).&lt;br /&gt;
&lt;br /&gt;
===Integration with htmleditor===&lt;br /&gt;
Html editor should be able to browse only relevant files - for example when editing resource introduction only images from the file area of that resource should be available; when editing html resource page only the content area images should be listed.&lt;br /&gt;
&lt;br /&gt;
There are several problems here:&lt;br /&gt;
# when adding new resource its context does not exist yet, we will have to create some table to handle temporary file storage for adding of new stuff, not easy but should be solvable - maybe we could abuse the course context id or store it temporarily in some special user file area&lt;br /&gt;
# we can not use absolute address relinking for pluginfile.php links, instead we can use the absolute links only when editing and before storage convert them to something like @@thispluginfile/intro@@/2112/112/image.jpg before storage. the local links would be converted to full absolute links before display or editing. Not all file areas will support this (ex: linking to assignment submission does not make sense because nobody else may access it anyway). This would allow us to implement image preview in html editor.&lt;br /&gt;
&lt;br /&gt;
Html editor should contain simplified single pane file manager with basic operations only - select file area, browse file area, upload file/copy user file/use repo file, delete. The editor will communicate with modules and core through ajax call to some script specified by module embedding the editor. The callback script would use different logic to construct the tree of files than the File manager, it needs to know only about files that other ppl viewing the resulting html may access.&lt;br /&gt;
&lt;br /&gt;
===Interactions with repos===&lt;br /&gt;
Repositories may serve as a replacement for file uploading. They may be also used to synchronise files between courses. The repo option should be available whenever there is a file upload field, sometimes with extra &amp;quot;keep synchronised&amp;quot; option (this would not make sense for stuff like assignment submissions).&lt;br /&gt;
&lt;br /&gt;
==Upgrade, migration and backwards compatibility==&lt;br /&gt;
It is going to be a pain again like DML/DDL ;-)&lt;br /&gt;
&lt;br /&gt;
===Code backwards compatibility===&lt;br /&gt;
0% backwards compatibility related to file storage. New objects will be mandatory to use. Old $CFG-&amp;gt;dataroot/$courseid/ will be empty, $CFG-&amp;gt;dataroot/blog/ too, etc.&lt;br /&gt;
&lt;br /&gt;
===Content backwards compatibility===&lt;br /&gt;
Means existing courses should not loose images, flash, etc. Though some new features (like resource sharing - if implemented) may not work with existing data that still uses files from course files area.&lt;br /&gt;
&lt;br /&gt;
There might be a breakage of links due to special characters stripping in uploaded files which will not match the links in uploaded html files any more. This should not be very common I hope.&lt;br /&gt;
&lt;br /&gt;
===Migration of content===&lt;br /&gt;
* resources - move files to new resource content file area; can be done automatically for pdf, image resources; definitely not accurate for uploaded web pages&lt;br /&gt;
* questions - image file moved to new are, image tag appended to questions&lt;br /&gt;
* moddata files - the easiest part, just move to new storage&lt;br /&gt;
* coursefiles - there might be many outdated files :-( :-(&lt;br /&gt;
* rss feeds links in readers - will be broken, the new security related code would break it anyway&lt;br /&gt;
&lt;br /&gt;
===Moving files to files table and file pool===&lt;br /&gt;
The migration process must be interruptable because it might take a very long time. The files would be moved from old location, the restarting would be straightforward.&lt;br /&gt;
Proposed stages:&lt;br /&gt;
#migration of all course files except moddata - finish marked by some $CFG-&amp;gt;files_migrated=true; - this step breaks the old file manager and html editor integration&lt;br /&gt;
#migration of blog attachments&lt;br /&gt;
#migration of question files&lt;br /&gt;
#migration of moddata files - each module is responsible to copy data from converted coursefiles or directly from moddata which is not converted automatically&lt;br /&gt;
&lt;br /&gt;
Some ppl use symbolic links in coursefiles - we must make sure that those will be copied to new storage in both places, though they can not be linked any more - anybody wanting to have content synced will need to move the files to some repository and set up the sync again.&lt;br /&gt;
&lt;br /&gt;
::Talked about a double task here, when migrating course files to module areas:&lt;br /&gt;
::# Parse html files to detect all the dependencies and move them together.&lt;br /&gt;
::# Fallback in pluginfile.php so, if something isn&#039;t found in module filearea, search for it in course filearea, copying it and finally, serving it.&lt;br /&gt;
&lt;br /&gt;
:: Also we talked about the possibility of add a new setting to resource in order to define if it should work against old coursefiles or new autocontained file areas. Migrated resources will point to old coursefiles while new ones will enforce autocontained file areas.&lt;br /&gt;
&lt;br /&gt;
:: it seems that only resource files will be really complex (because allow arbitrary HTML inclusion). The rest (labels, intros... doesn&#039;t) and should be easier to parse.&lt;br /&gt;
&lt;br /&gt;
::[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 19:00, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
==Backup/restore changes==&lt;br /&gt;
File handling in backups needs to be fully rewritten - list of files in xml + pool of sha1 named files with contents. This solves the utf-8 trouble here, yay!!&lt;br /&gt;
&lt;br /&gt;
==Quotas==&lt;br /&gt;
File size will be stored in files table, we can use simple queries to find out how much space is used, however this may not be accurate because the sha1 hash based storage eliminates duplicate files.&lt;br /&gt;
::If sha1 string is stored in the files table (non-unique), then it can be used to detect duplicates within the files table and only counting their size once. To avoid having to search this often, we could do it periodically and store the filesize with each file record (only the first of the duplicates gets a filesize, the others get 0). [[User:Nicolas Connault|Nicolas Connault]]&lt;br /&gt;
&lt;br /&gt;
*total course files - find out all contexts used in course, query files table with contextid IN ($listofcontexts)&lt;br /&gt;
*module files - find module context and calculate space per file area&lt;br /&gt;
*user files quota - inside the personal area only, counting all attachments in all mods might take a while&lt;br /&gt;
We could also divide the file size by number of instances that are using it, this might be considered more accurate in some scenarios.&lt;br /&gt;
&lt;br /&gt;
==Other==&lt;br /&gt;
*antivirus scanning + upload manager rewrite/integration with forms lib&lt;br /&gt;
*zip compression and extraction&lt;br /&gt;
&lt;br /&gt;
==Major problems==&lt;br /&gt;
List of hard to solve prolbems&lt;br /&gt;
&lt;br /&gt;
===unicode zip support===&lt;br /&gt;
Unicode chars in zip files uploaded by teachers - unfortunately there is no 100% solution that will work for anybody because most zip programs do not support unicode, it is usually &#039;&#039;garbage in/garbage out&#039;&#039; which works in some cases only&lt;br /&gt;
&lt;br /&gt;
Latest WinZIP 11.2 and Total Commander seem to support some very limited form of utf-8 encodings of file names. I managed to create a zip file in Windows (Czech locale) and extract them in linux with native PHP zip functions, the only step I needed to add was conversion cp852(DOS charset for Czech locale) -&amp;gt; UTF-8. The native windows zipping did not work for me though, because it does some different borking of charsets.&lt;br /&gt;
&lt;br /&gt;
In any case it seems likely that native PHP support in PHP 5.2.x should be better than current pclzip or infozip binary.&lt;br /&gt;
&lt;br /&gt;
===empty directories===&lt;br /&gt;
Hmm, thinking a bit more about Justin&#039;s comment I realised there is no support for empty directories in this proposal. This will require either new table or some hack in files table - maybe we could add files with &amp;quot;.&amp;quot; as name and just skip them when iterating directory content.&lt;br /&gt;
&lt;br /&gt;
===file overwriting===&lt;br /&gt;
Concept of file overwriting does not exist anymore here, the path+filename are not enforced to be unique - we can not make index because sloppy mssql does not allow indexes larger than 900 bytes :-( We will haev to emulate it somehow and deal with collisions if found.&lt;br /&gt;
&lt;br /&gt;
== Some little comments to be considered (to avoid forgetting them) ==&lt;br /&gt;
&lt;br /&gt;
* each context will have its own &amp;quot;file manager&amp;quot;&lt;br /&gt;
* separate &amp;quot;file manager context&amp;quot; files (FMF) and &amp;quot;internal context&amp;quot; (ICF) files (current modedit files, submissions, attachements...)&lt;br /&gt;
* /pluginfile.php/SYSCONTEXTID/{blog|question} and so... will have own FMF too? Or only ICF ?&lt;br /&gt;
* Way to copy between contexts&lt;br /&gt;
* Links = -1 for them&lt;br /&gt;
* Deletion strategy (locks, quarantine status...)&lt;br /&gt;
* include support for quotas per user, per course, etc &lt;br /&gt;
* upgrade process should be interruptable (like the unicode upgrade) so it can be stopped/restarted any time&lt;br /&gt;
&lt;br /&gt;
==Justin&#039;s thinking out loud==&lt;br /&gt;
&lt;br /&gt;
I&#039;m actually working on implementing this along with extending an existing Alfresco integration to work together with the whole File / Repository system and I wanted to get some of my comments and thoughts in here for feedback.  Go easy on me.  =)&lt;br /&gt;
&lt;br /&gt;
So far I&#039;ve only got one that I&#039;d like to solicit some feedback on (BTW, if this would be better suited to a forum discussion, let me know):&lt;br /&gt;
&lt;br /&gt;
===Not storing the full &#039;&#039;filepath&#039;&#039; with each entry in the &#039;&#039;&#039;file&#039;&#039;&#039; table===&lt;br /&gt;
*For browsing a directory structure, determining things like child directories or a parent directory given a filepath requires a lot of extraneous coding in PHP.  I think it might be better served to create a new &#039;&#039;&#039;file_directory&#039;&#039;&#039; table, storing only a directory name, and reference to a parent directory record.  The benefits here are that we&#039;re storing a lot of duplicate text field values in the &#039;&#039;&#039;file&#039;&#039;&#039; table and browsing through the file picker for local files doesn&#039;t require a lot of PHP overhead to calculate links to parent / child directories.&lt;br /&gt;
*Given that file permissions are no longer calculated using structured file paths, using the complete, full, path to a given file would most likely never be needed.&lt;br /&gt;
&lt;br /&gt;
*The &#039;&#039;&#039;repositorypath&#039;&#039;&#039; field in the &#039;&#039;&#039;repository_sync&#039;&#039;&#039; table still makes sense, though.&lt;br /&gt;
&lt;br /&gt;
*The &#039;&#039;&#039;file_directory&#039;&#039;&#039; 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;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;parent&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|ID of directory that this record is a child of.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;directoryname&#039;&#039;&#039;&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The actual name of this directory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
skodak: filepath is stored in files table - its root is the corresponding filearea, the file manager will use the context tree to find all plugins/courses and ask them to return the list of areas with all those small branches inside it&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Development:Repository API]]&lt;br /&gt;
* [[Development:Portfolio API]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:File_API&amp;diff=39146</id>
		<title>Development:File API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:File_API&amp;diff=39146"/>
		<updated>2008-07-03T14:58:07Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: fixing grammar&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page outlines the current thinking about implementing file storage and access in Moodle 2.0.   It&#039;s a SPECIFICATION UNDER CONSTRUCTION!&lt;br /&gt;
&lt;br /&gt;
The page is open for everyone so everyone can help correct mistakes and help with the evolution of this document.  However, if you have questions, problems to report or major changes to suggest please add them to the [[Development_talk:File_API|page comments]], or start a discussion in the [http://moodle.org/mod/forum/view.php?id=1807 Repositories forum].  We&#039;ll endeavour to merge all such suggestions into the main spec before we start development.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objectives==&lt;br /&gt;
&lt;br /&gt;
# Allow files to be added directly into Moodle (as we do now)&lt;br /&gt;
# Remember where files came from&lt;br /&gt;
# Give modules control over the access to files using capabilities and other local rules&lt;br /&gt;
# Consistent and simple approach for ALL file handling throughout Moodle&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The File API is a core set of interfaces that all Moodle code will use to:&lt;br /&gt;
# store files within Moodle&lt;br /&gt;
# display files to Moodle users&lt;br /&gt;
&lt;br /&gt;
It applies only to &amp;quot;user&amp;quot; files.  It will NOT apply to local files and caches created by Moodle such as these directories in dataroot: temp, lang, cache, environment, filter, rss, search, sessions, upgradelogs etc&lt;br /&gt;
&lt;br /&gt;
The API will be split into several independent parts:&lt;br /&gt;
# File serving API&lt;br /&gt;
## file.php&lt;br /&gt;
## pluginfile.php&lt;br /&gt;
## userfile.php&lt;br /&gt;
## rssfile.php&lt;br /&gt;
# File storage API&lt;br /&gt;
## optional access control&lt;br /&gt;
## optional repo sync&lt;br /&gt;
# File management API&lt;br /&gt;
## File browsing&lt;br /&gt;
## File linking (editor integration)&lt;br /&gt;
## Upload from repository&lt;br /&gt;
&lt;br /&gt;
==File serving API==&lt;br /&gt;
Deals with serving of files - browser requests file, Moodle sends it back. We have three main files. It is important to setup slasharguments on server (file.php/some/thing/xxx.jpg), any content that relies on relative links can not work without it (scorm, uploaded html pages, etc.).&lt;br /&gt;
&lt;br /&gt;
===file.php===&lt;br /&gt;
Serves course files.&lt;br /&gt;
&lt;br /&gt;
Implements basic file access. Ideally only images and files linked from course sections should be there, no XSS protection required - we expect javascript, sw, etc. there, no way to make it &amp;quot;secure&amp;quot;. The access control is not critical any more if we move most of the files into modules&lt;br /&gt;
&lt;br /&gt;
The file name and parameter structure is critical for backwards compatibility of existing course content.&lt;br /&gt;
&lt;br /&gt;
 /file.php/courseid/dir/dir/filename.ext&lt;br /&gt;
&lt;br /&gt;
Internally the files would be stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;$coursecontextid, &#039;filearea&#039;=&amp;gt;&#039;content&#039;, &#039;itemid&#039;=&amp;gt;0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===pluginfile.php===&lt;br /&gt;
(aka modfile.php)&lt;br /&gt;
Sends module, block, question files.&lt;br /&gt;
* modules decide about access control&lt;br /&gt;
* optional XSS protection - student submitted files must not be served with normal headers, we have to force download instead; ideally there should be second wwwroot for serving of untrusted files&lt;br /&gt;
* only internal links to selected areas are supported - you can link images in summary area, but not the assignment submissions&lt;br /&gt;
&lt;br /&gt;
Absolute file links need to be rewritten if html editing allowed in module. The links are stored internally as relative links. Before editing or display the internal link representation is converted to absolute links using simple str_replace() @@thipluginlink/summary@@/image.jpg --&amp;gt; /pluginfile.php/assignmentcontextid/summary/image.jpg, it is converted back to internal links before saving.&lt;br /&gt;
&lt;br /&gt;
::Can the distinct file areas supported by one plugin be declared somehow in order add some information about them? For example, I think it can be interesting to declare:&lt;br /&gt;
::* assignment_summary:&lt;br /&gt;
::** relpath=&#039;summary&#039;&lt;br /&gt;
::** userdata=false&lt;br /&gt;
::** anotherproperty=anothervalue&lt;br /&gt;
::* assignment_submission:&lt;br /&gt;
::** relpath=&#039;submission/@@USERID@@&#039;&lt;br /&gt;
::** userdata=false&lt;br /&gt;
::** anotherproperty=anothervalue&lt;br /&gt;
::* and so on...&lt;br /&gt;
::And then, when the editor &amp;quot;receives&amp;quot; one &amp;quot;assignment_summary&amp;quot; areaname, if knows what to show and so on? Also that info could be useful to know, in backup &amp;amp; restore if some fileareas have to be processed or no (userdata=false). Or also, when reconstructing the links (str_replace() above). And will cause to have a well defined list of fileareas by module, instead of coding them in a free way (prone to errors). [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 16:35, 28 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
::Something like this will be part of file management API, hardcoding this in file storage would make it less flexible imo [[User:Skodak|Skodak]]&lt;br /&gt;
&lt;br /&gt;
::Yup, yup. Storage doesn&#039;t know anything but get/put files (nothing else). It&#039;s part of management, absolutely. [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 11:21, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/contextid/areaname/arbitrary/params/or/dirs/filename.ext&lt;br /&gt;
&lt;br /&gt;
pluginfile.php detects the type of plugin from context table, fetches basic info (like $course or $cm if appropriate) and calls plugin function (or later method) which does the access control and finally sends the file to user. &#039;&#039;areaname&#039;&#039; separates files by type and divides the context into several subtrees - for example &#039;&#039;summary&#039;&#039; files (images used in module intros), post attachments, etc.&lt;br /&gt;
&lt;br /&gt;
====assignment example====&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/summary/someimage.jpg&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/submission/submissionid/attachmentname.ext&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/extra/allsubmissionfiles.zip&lt;br /&gt;
&lt;br /&gt;
::Uhm... all those files together? What&#039;s going to differentiate the &amp;quot;submission&amp;quot; path in the example above from the &amp;quot;summary&amp;quot; path? Is it supposed that the editor, or the filemanager won&#039;t allow , for example to pick-up one file from the &amp;quot;submission&amp;quot; area to be used in the summary of one assignment and only the &amp;quot;summary&amp;quot; area will be showed? That means multiple file managers by context and it&#039;s against the clean &amp;quot;one file manager per context&amp;quot; agreed below [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 21:28, 26 June 2008 (CDT)&lt;br /&gt;
::Yes Eloy, the different areas (summary, submission) etc. have different uses, different access control. There are two types of file manager - the two pane file manager which lists all contexts+areas user may access, and minimalistic manager in html editor which shows only subset of areas from current plugin (because you can not link anything else).&lt;br /&gt;
&lt;br /&gt;
====scorm example====&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/scormcontextid/summary/someimage.jpg&lt;br /&gt;
 /pluginfile.php/scormcontextid/content/revisionnumber/dir/somescormfile.js&lt;br /&gt;
&lt;br /&gt;
The revision counter is incremented when any file changes in order to prevent caching problems. The lifetime should be adjustable in module settings.&lt;br /&gt;
&lt;br /&gt;
====quiz example====&lt;br /&gt;
&lt;br /&gt;
 pluginfile.php/quizcontextid/summary/niceimage.jpg&lt;br /&gt;
 pluginfile.php/quizcontextid/report/type/export.ods&lt;br /&gt;
&lt;br /&gt;
====questions example====&lt;br /&gt;
&lt;br /&gt;
 pluginfile.php/SYSCONTEXTID/question/questionid/file.jpg&lt;br /&gt;
&lt;br /&gt;
====blog example====&lt;br /&gt;
Blog entries or notes in general do not have context id (because they live in system context, SYSCONTEXTID below is the id of system context).&lt;br /&gt;
The note attachments are always served with XSS protection on, ideally we should use separate wwwroot for this. Access control can be hardcoded.&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/SYSCONTEXTID/blog/blogentryid/attachmentname.ext&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;SYSCONTEXTID, &#039;filearea&#039;=&amp;gt;&#039;blog&#039;, &#039;itemid&#039;=&amp;gt;$blogentryid)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====backup example====&lt;br /&gt;
It would be nice to have some special protection of backup files - new capabilities for backup file download, upload. Backups contain a lot of personal info, we could block restoring of backups from other sites too.&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/coursecontextid/backup/backupfile.zip&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;$coursecontextid, &#039;filearea&#039;=&amp;gt;&#039;backup&#039;, &#039;itemid&#039;=&amp;gt;0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===userfile.php===&lt;br /&gt;
Personal file storage, intended as an online storage of work in progress like assignments before the submission.&lt;br /&gt;
* read/write own files only for now&lt;br /&gt;
* option to share with others later&lt;br /&gt;
* personal &amp;quot;websites&amp;quot; will not be supported (security)&lt;br /&gt;
&lt;br /&gt;
 /userfile.php/userid/dir/dir/filename.ext&lt;br /&gt;
&lt;br /&gt;
===rssfile.php===&lt;br /&gt;
Replaces rss/file.php which is kept only for backwards compatibility.&lt;br /&gt;
RSS files should not require sessions/cookies, URLs should contain some sort of security token/key.&lt;br /&gt;
Internally the files may be stored in database or together with other files.&lt;br /&gt;
Performance improvements - we should support both Etag (cool) and Last-Modified (more used), when we receive If-None-Match/If-Modified-Since =&amp;gt; 304 &lt;br /&gt;
&lt;br /&gt;
 /rssfile.php/contextid/any/parameters/module/wants/rss.xml&lt;br /&gt;
 /rssfile.php/SYSCONTEXTID/blog/userid/rss.xml&lt;br /&gt;
&lt;br /&gt;
Again modules and plugins decide what gets sent to user.&lt;br /&gt;
&lt;br /&gt;
===Temporary files===&lt;br /&gt;
Temporary files are usually used during the lifetime of one script only.&lt;br /&gt;
uses:&lt;br /&gt;
* exports&lt;br /&gt;
* imports&lt;br /&gt;
* zipping/unzipping&lt;br /&gt;
* processing by executable files (latex, mimetex)&lt;br /&gt;
&lt;br /&gt;
Ideally these files should never use utf-8 (which is a major problem for zipping at the moment).&lt;br /&gt;
Proposed new sha1 based file storage is not suitable both for performance and technical reasons.&lt;br /&gt;
&lt;br /&gt;
===Legacy file storage and serving===&lt;br /&gt;
Going to use good-old separate directories in $CFG-&amp;gt;dataroot.&lt;br /&gt;
&lt;br /&gt;
file serving and storage:&lt;br /&gt;
# user avatars - user/pix.php&lt;br /&gt;
# group avatars - user/pixgroup.php&lt;br /&gt;
# tex, algebra - filter/tex/* and filter/algebra/*&lt;br /&gt;
# rss cache (?full rss rewrite soon?) - backwards compatibility only rss/file.php&lt;br /&gt;
&lt;br /&gt;
only storage:&lt;br /&gt;
#sessions&lt;br /&gt;
&lt;br /&gt;
==File storage API==&lt;br /&gt;
Modules in general work only with local Moodle files. One of the major reason is performance when accessing external repository files. It will be possible to use repositories instead of file uploading and also to keep local files synced with external repository.&lt;br /&gt;
&lt;br /&gt;
File contents are stored in moodledata/filepool indexed using SHA1 hashes instead of file names; file names, relative paths and other metadata will be stored in file(_xxx) database tables. This should be fully abstracted so that modules do not actually know where the files are located. When storing files the content is sent as string or file handle, when reading content it is returned as file handle.&lt;br /&gt;
&lt;br /&gt;
===files table===&lt;br /&gt;
&lt;br /&gt;
This table contains one entry for every file.  Enough information is kept here so that the file can be fully identified and retrieved again if necessary.&lt;br /&gt;
&lt;br /&gt;
note: plural used because file is a reserved word&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|sha1hash&lt;br /&gt;
|varchar(40)&lt;br /&gt;
| &lt;br /&gt;
|The sha1 hash of content.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;contextid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|The context id defined in context table - identifies the instance of plugin owning the file.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filearea&lt;br /&gt;
|varchar(50)&lt;br /&gt;
|&lt;br /&gt;
|Like &amp;quot;submissions&amp;quot;, &amp;quot;intro&amp;quot; and &amp;quot;content&amp;quot; (images and swf linked from summaries), etc.; &amp;quot;blogs&amp;quot; and &amp;quot;userfiles&amp;quot; are special case that live at the system context.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|itemid&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|Some plugin specific item id (eg. forum post, blog entry or assignment submission or user id for user files)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filepath&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|relative path to file from module content root, useful in Scorm and Resource mod - most of the mods do not need this&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filename&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The full Unicode name of this file (case sensitive)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filesize&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|size of file - bytes&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|mimetype&lt;br /&gt;
|varchar(100)&lt;br /&gt;
|NULL&lt;br /&gt;
|type of file&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;userid&#039;&#039;&#039;&lt;br /&gt;
|int(10)  &lt;br /&gt;
|NULL&lt;br /&gt;
|Optional - general user id field - meaning depending on plugin&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The time this file was created&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The last time the file was modified&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
index on &amp;quot;contextid, filearea, itemid&amp;quot; and &amp;quot;sha1hash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Plugin type is not specified because it is derived from contextid, items like blog that do not have own context will use own filearea usually from systemcontextid.&lt;br /&gt;
&lt;br /&gt;
::Perhpas we could also hash filepath and filename and index by them, to save some text limitations in the DB side (length limits of indexes, not indexable, complex retrieval...). [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 11:54, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
::Also, perhaps we should store finally the plugin type there to save some queries per request, using it to drive to the correct file handling of each plugin. [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 18:54, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
=== files_cleanup table ===&lt;br /&gt;
&lt;br /&gt;
This table contains candidates for deletion from the file pool. Files are not deleted immediately, cron uses the files_cleanup table, verifies the file is not used any more and deletes it from pool. Reasons for cron clean-up are performance and prevention of collision - there could be a problem with concurrent uploads and deletes, we will probably need to add some table-based locking during the clean-up.&lt;br /&gt;
&lt;br /&gt;
We might add an extra script that does deep validation of pool area - report missing files, report orphaned files, content not matching the sha1 filename, etc. - this would very very time consuming.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|sha1hash&lt;br /&gt;
|varchar(40)&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== files_metadata table ===&lt;br /&gt;
&lt;br /&gt;
This table contains extra metadata about files.  Repositories could provide this, or it could be manually edited in the local copy.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;fileid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|Id of file.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;name&#039;&#039;&#039;&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The name of extra metadata&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|value&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|Value&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===files_acl table===&lt;br /&gt;
&lt;br /&gt;
This table describes optional ACL for file. This is not required in majority of cases, modules usually hardcode the file access logic, course files should not be used much any more.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;fileid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
| &lt;br /&gt;
|The file we are defining access for&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;contextid&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The context where this file is being published&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;capability&#039;&#039;&#039;&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|The capability that is required to see this file.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====acl notes====&lt;br /&gt;
* this is missing some concept similar to &#039;&#039;&#039;user/group/others&#039;&#039;&#039;, for example in case of user files typical user can not assign permissions or view them - this becomes useless there&lt;br /&gt;
* it is more important to synchronise the availability of file link and the file itself - having link pointing to inaccessible file or file which is accessible when not wanted are both problems&lt;br /&gt;
* browser/proxy caching works against us here - &amp;quot;secret&amp;quot; files should not be cached&lt;br /&gt;
&lt;br /&gt;
===files_sync table===&lt;br /&gt;
&lt;br /&gt;
This table contains information on how to synchronise data with repositories. Data would be synchronised from cron.php or on demand from file manager. The sync would be one way only (repository--&amp;gt;local file).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;fileid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|Id of file.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;repositoryid&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The repository instance this is associated with, see [[Development:Repository_API]]&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|updates&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|Specifies the update schedule (0 = none, 1 = on demand, other = some period in seconds)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|repositorypath&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|The full path to the original file on the repository&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timeimportfirst&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The first time this file was imported into Moodle&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timeimportlast&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The most recent time that this file was imported into Moodle&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===File content storage===&lt;br /&gt;
Originally the file storage hierarchy contained a lot of metadata including userids, entry ids, filenames, etc. The file content will now be stored separately from file metadata. It must supports utf8 on all platforms.&lt;br /&gt;
&lt;br /&gt;
File storing:&lt;br /&gt;
# calculate SHA1 hash of content&lt;br /&gt;
# check if file with SHA1 name exists, if not add the file to file pool&lt;br /&gt;
# remove SHA1 from list of deleted files if found there&lt;br /&gt;
# store file in &#039;&#039;file&#039;&#039; table, use SHA1 as file pool identifier&lt;br /&gt;
&lt;br /&gt;
File reading:&lt;br /&gt;
#fetch file record from &#039;file&#039; table - probably using file id or combination of contextid+instanceid&lt;br /&gt;
#fetch content of file &lt;br /&gt;
&lt;br /&gt;
File deleting:&lt;br /&gt;
#delete record from &#039;&#039;file&#039;&#039; table, remember file SHA1&lt;br /&gt;
#store the deleted SHA1 in deleted files table, do not remove the physical file yet&lt;br /&gt;
#wait for cron cleanup script to actually delete the file named SHA1 (proper table locking needed to prevent race conditions when adding/deleting files)&lt;br /&gt;
&lt;br /&gt;
====File pool details====&lt;br /&gt;
located in $CFG-&amp;gt;dataroot/filepool/, all files can not be stored in one directory due to OS limitations, it uses 3 levels based on first three characters of sha1 hash. It is unlikely that there will be thousands of files with the same first 3 chars in sha1 hash of their content.&lt;br /&gt;
&lt;br /&gt;
This type of storage saves a lot of disk space when storing multiple copies of the same large file. It can also help substantially when synchronising data with external repositories. Another benefit is we can detect inconsistencies in file content.&lt;br /&gt;
&lt;br /&gt;
File read performance is similar to previous code, file write performance will be slower - due to hashing and extra database access.&lt;br /&gt;
&lt;br /&gt;
 dataroot &lt;br /&gt;
    /filepool&lt;br /&gt;
       /00&lt;br /&gt;
       /01&lt;br /&gt;
       ...&lt;br /&gt;
       /&#039;&#039;&#039;23&#039;&#039;&#039;&lt;br /&gt;
         /00&lt;br /&gt;
         /01&lt;br /&gt;
         ...&lt;br /&gt;
         /&#039;&#039;&#039;1e&#039;&#039;&#039;&lt;br /&gt;
            /00&lt;br /&gt;
            /01&lt;br /&gt;
            ...&lt;br /&gt;
            /&#039;&#039;&#039;2d&#039;&#039;&#039;&lt;br /&gt;
               /231e2dc421be4fcd0172e5afceea3970e2f3d940.jpg&lt;br /&gt;
       ...&lt;br /&gt;
       /fe&lt;br /&gt;
       /ff&lt;br /&gt;
&lt;br /&gt;
==File management API==&lt;br /&gt;
&lt;br /&gt;
This section describes following:&lt;br /&gt;
#file manager&lt;br /&gt;
#integration with html editor&lt;br /&gt;
#interactions with repos&lt;br /&gt;
&lt;br /&gt;
===File manager===&lt;br /&gt;
Single pane file manager is hard to implement without drag &amp;amp; drop which is notoriously problematic in web based applications. I propose to implement a two pane commander-style file manager. Two pane manager allows you to easily copy/move files between two different contexts (ex: courses).&lt;br /&gt;
&lt;br /&gt;
File manager must not interact directly with filesystem API, instead each module should return traversable tree of files and directories with both real and localised names (localised names are needed for dirs like backupdata).&lt;br /&gt;
&lt;br /&gt;
Originally there was a single file tree for each course. We need to fully separate each module/block from the course files and there might be also independent file areas in modules (ex: module introduction, content files, submissions, post attachments). File area may be defined as a small tree where we can use relative paths. These file areas are hanging from the branches of the context tree (this needs a picture).&lt;br /&gt;
&lt;br /&gt;
===Integration with htmleditor===&lt;br /&gt;
Html editor should be able to browse only relevant files - for example when editing resource introduction only images from the file area of that resource should be available; when editing html resource page only the content area images should be listed.&lt;br /&gt;
&lt;br /&gt;
There are several problems here:&lt;br /&gt;
# when adding new resource its context does not exist yet, we will have to create some table to handle temporary file storage for adding of new stuff, not easy but should be solvable - maybe we could abuse the course context id or store it temporarily in some special user file area&lt;br /&gt;
# we can not use absolute address relinking for pluginfile.php links, instead we can use the absolute links only when editing and before storage convert them to something like @@thispluginfile/intro@@/2112/112/image.jpg before storage. the local links would be converted to full absolute links before display or editing. Not all file areas will support this (ex: linking to assignment submission does not make sense because nobody else may access it anyway). This would allow us to implement image preview in html editor.&lt;br /&gt;
&lt;br /&gt;
Html editor should contain simplified single pane file manager with basic operations only - select file area, browse file area, upload file/copy user file/use repo file, delete. The editor will communicate with modules and core through ajax call to some script specified by module embedding the editor. The callback script would use different logic to construct the tree of files than the File manager, it needs to know only about files that other ppl viewing the resulting html may access.&lt;br /&gt;
&lt;br /&gt;
===Interactions with repos===&lt;br /&gt;
Repositories may serve as a replacement for file uploading. They may be also used to synchronise files between courses. The repo option should be available whenever there is a file upload field, sometimes with extra &amp;quot;keep synchronised&amp;quot; option (this would not make sense for stuff like assignment submissions).&lt;br /&gt;
&lt;br /&gt;
==Upgrade, migration and backwards compatibility==&lt;br /&gt;
It is going to be a pain again like DML/DDL ;-)&lt;br /&gt;
&lt;br /&gt;
===Code backwards compatibility===&lt;br /&gt;
0% backwards compatibility related to file storage. New objects will be mandatory to use. Old $CFG-&amp;gt;dataroot/$courseid/ will be empty, $CFG-&amp;gt;dataroot/blog/ too, etc.&lt;br /&gt;
&lt;br /&gt;
===Content backwards compatibility===&lt;br /&gt;
Means existing courses should not loose images, flash, etc. Though some new features (like resource sharing - if implemented) may not work with existing data that still uses files from course files area.&lt;br /&gt;
&lt;br /&gt;
There might be a breakage of links due to special characters stripping in uploaded files which will not match the links in uploaded html files any more. This should not be very common I hope.&lt;br /&gt;
&lt;br /&gt;
===Migration of content===&lt;br /&gt;
* resources - move files to new resource content file area; can be done automatically for pdf, image resources; definitely not accurate for uploaded web pages&lt;br /&gt;
* questions - image file moved to new are, image tag appended to questions&lt;br /&gt;
* moddata files - the easiest part, just move to new storage&lt;br /&gt;
* coursefiles - there might be many outdated files :-( :-(&lt;br /&gt;
* rss feeds links in readers - will be broken, the new security related code would break it anyway&lt;br /&gt;
&lt;br /&gt;
===Moving files to files table and file pool===&lt;br /&gt;
The migration process must be interruptable because it might take a very long time. The files would be moved from old location, the restarting would be straightforward.&lt;br /&gt;
Proposed stages:&lt;br /&gt;
#migration of all course files except moddata - finish marked by some $CFG-&amp;gt;files_migrated=true; - this step breaks the old file manager and html editor integration&lt;br /&gt;
#migration of blog attachments&lt;br /&gt;
#migration of question files&lt;br /&gt;
#migration of moddata files - each module is responsible to copy data from converted coursefiles or directly from moddata which is not converted automatically&lt;br /&gt;
&lt;br /&gt;
Some ppl use symbolic links in coursefiles - we must make sure that those will be copied to new storage in both places, though they can not be linked any more - anybody wanting to have content synced will need to move the files to some repository and set up the sync again.&lt;br /&gt;
&lt;br /&gt;
::Talked about a double task here, when migrating course files to module areas:&lt;br /&gt;
::# Parse html files to detect all the dependencies and move them together.&lt;br /&gt;
::# Fallback in pluginfile.php so, if something isn&#039;t found in module filearea, search for it in course filearea, copying it and finally, serving it.&lt;br /&gt;
&lt;br /&gt;
:: Also we talked about the possibility of add a new setting to resource in order to define if it should work against old coursefiles or new autocontained file areas. Migrated resources will point to old coursefiles while new ones will enforce autocontained file areas.&lt;br /&gt;
&lt;br /&gt;
:: it seems that only resource files will be really complex (because allow arbitrary HTML inclusion). The rest (labels, intros... doesn&#039;t) and should be easier to parse.&lt;br /&gt;
&lt;br /&gt;
::[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 19:00, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
==Backup/restore changes==&lt;br /&gt;
File handling in backups needs to be fully rewritten - list of files in xml + pool of sha1 named files with contents. This solves the utf-8 trouble here, yay!!&lt;br /&gt;
&lt;br /&gt;
==Quotas==&lt;br /&gt;
File size will be stored in files table, we can use simple queries to find out how much space is used, however this may not be accurate because the sha1 hash based storage eliminates duplicate files.&lt;br /&gt;
*total course files - find out all contexts used in course, query files table with contextid IN ($listofcontexts)&lt;br /&gt;
*module files - find module context and calculate space per file area&lt;br /&gt;
*user files quota - inside the personal area only, counting all attachments in all mods might take a while&lt;br /&gt;
We could also divide the file size by number of instances that are using it, this might be considered more accurate in some scenarios.&lt;br /&gt;
&lt;br /&gt;
==Other==&lt;br /&gt;
*antivirus scanning + upload manager rewrite/integration with forms lib&lt;br /&gt;
*zip compression and extraction&lt;br /&gt;
&lt;br /&gt;
==Major problems==&lt;br /&gt;
List of hard to solve prolbems&lt;br /&gt;
&lt;br /&gt;
===unicode zip support===&lt;br /&gt;
Unicode chars in zip files uploaded by teachers - unfortunately there is no 100% solution that will work for anybody because most zip programs do not support unicode, it is usually &#039;&#039;garbage in/garbage out&#039;&#039; which works in some cases only&lt;br /&gt;
&lt;br /&gt;
Latest WinZIP 11.2 and Total Commander seem to support some very limited form of utf-8 encodings of file names. I managed to create a zip file in Windows (Czech locale) and extract them in linux with native PHP zip functions, the only step I needed to add was conversion cp852(DOS charset for Czech locale) -&amp;gt; UTF-8. The native windows zipping did not work for me though, because it does some different borking of charsets.&lt;br /&gt;
&lt;br /&gt;
In any case it seems likely that native PHP support in PHP 5.2.x should be better than current pclzip or infozip binary.&lt;br /&gt;
&lt;br /&gt;
===empty directories===&lt;br /&gt;
Hmm, thinking a bit more about Justin&#039;s comment I realised there is no support for empty directories in this proposal. This will require either new table or some hack in files table - maybe we could add files with &amp;quot;.&amp;quot; as name and just skip them when iterating directory content.&lt;br /&gt;
&lt;br /&gt;
===file overwriting===&lt;br /&gt;
Concept of file overwriting does not exist anymore here, the path+filename are not enforced to be unique - we can not make index because sloppy mssql does not allow indexes larger than 900 bytes :-( We will haev to emulate it somehow and deal with collisions if found.&lt;br /&gt;
&lt;br /&gt;
== Some little comments to be considered (to avoid forgetting them) ==&lt;br /&gt;
&lt;br /&gt;
* each context will have its own &amp;quot;file manager&amp;quot;&lt;br /&gt;
* separate &amp;quot;file manager context&amp;quot; files (FMF) and &amp;quot;internal context&amp;quot; (ICF) files (current modedit files, submissions, attachements...)&lt;br /&gt;
* /pluginfile.php/SYSCONTEXTID/{blog|question} and so... will have own FMF too? Or only ICF ?&lt;br /&gt;
* Way to copy between contexts&lt;br /&gt;
* Links = -1 for them&lt;br /&gt;
* Deletion strategy (locks, quarantine status...)&lt;br /&gt;
* include support for quotas per user, per course, etc &lt;br /&gt;
* upgrade process should be interruptable (like the unicode upgrade) so it can be stopped/restarted any time&lt;br /&gt;
&lt;br /&gt;
==Justin&#039;s thinking out loud==&lt;br /&gt;
&lt;br /&gt;
I&#039;m actually working on implementing this along with extending an existing Alfresco integration to work together with the whole File / Repository system and I wanted to get some of my comments and thoughts in here for feedback.  Go easy on me.  =)&lt;br /&gt;
&lt;br /&gt;
So far I&#039;ve only got one that I&#039;d like to solicit some feedback on (BTW, if this would be better suited to a forum discussion, let me know):&lt;br /&gt;
&lt;br /&gt;
===Not storing the full &#039;&#039;filepath&#039;&#039; with each entry in the &#039;&#039;&#039;file&#039;&#039;&#039; table===&lt;br /&gt;
*For browsing a directory structure, determining things like child directories or a parent directory given a filepath requires a lot of extraneous coding in PHP.  I think it might be better served to create a new &#039;&#039;&#039;file_directory&#039;&#039;&#039; table, storing only a directory name, and reference to a parent directory record.  The benefits here are that we&#039;re storing a lot of duplicate text field values in the &#039;&#039;&#039;file&#039;&#039;&#039; table and browsing through the file picker for local files doesn&#039;t require a lot of PHP overhead to calculate links to parent / child directories.&lt;br /&gt;
*Given that file permissions are no longer calculated using structured file paths, using the complete, full, path to a given file would most likely never be needed.&lt;br /&gt;
&lt;br /&gt;
*The &#039;&#039;&#039;repositorypath&#039;&#039;&#039; field in the &#039;&#039;&#039;repository_sync&#039;&#039;&#039; table still makes sense, though.&lt;br /&gt;
&lt;br /&gt;
*The &#039;&#039;&#039;file_directory&#039;&#039;&#039; 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;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;parent&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|ID of directory that this record is a child of.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;directoryname&#039;&#039;&#039;&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The actual name of this directory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
skodak: filepath is stored in files table - its root is the corresponding filearea, the file manager will use the context tree to find all plugins/courses and ask them to return the list of areas with all those small branches inside it&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Development:Repository API]]&lt;br /&gt;
* [[Development:Portfolio API]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/test/index.php?title=Development:File_API&amp;diff=39145</id>
		<title>Development:File API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/test/index.php?title=Development:File_API&amp;diff=39145"/>
		<updated>2008-07-03T14:56:04Z</updated>

		<summary type="html">&lt;p&gt;Nicolasconnault: fixing typos&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;This page outlines the current thinking about implementing file storage and access in Moodle 2.0.   It&#039;s a SPECIFICATION UNDER CONSTRUCTION!&lt;br /&gt;
&lt;br /&gt;
The page is open for everyone so everyone can help correct mistakes and help with the evolution of this document.  However, if you have questions, problems to report or major changes to suggest please add them to the [[Development_talk:File_API|page comments]], or start a discussion in the [http://moodle.org/mod/forum/view.php?id=1807 Repositories forum].  We&#039;ll endeavour to merge all such suggestions into the main spec before we start development.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Objectives==&lt;br /&gt;
&lt;br /&gt;
# Allow files to be added directly into Moodle (as we do now)&lt;br /&gt;
# Remember where files came from&lt;br /&gt;
# Give modules control over the access to files using capabilities and other local rules&lt;br /&gt;
# Consistent and simple approach for ALL file handling throughout Moodle&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The File API is a core set of interfaces that all Moodle code will use to:&lt;br /&gt;
# store files within Moodle&lt;br /&gt;
# display files to Moodle users&lt;br /&gt;
&lt;br /&gt;
It applies only to &amp;quot;user&amp;quot; files.  It will NOT apply to local files and caches created by Moodle such as these directories in dataroot: temp, lang, cache, environment, filter, rss, search, sessions, upgradelogs etc&lt;br /&gt;
&lt;br /&gt;
The API will be split into several independent parts:&lt;br /&gt;
# File serving API&lt;br /&gt;
## file.php&lt;br /&gt;
## pluginfile.php&lt;br /&gt;
## userfile.php&lt;br /&gt;
## rssfile.php&lt;br /&gt;
# File storage API&lt;br /&gt;
## optional access control&lt;br /&gt;
## optional repo sync&lt;br /&gt;
# File management API&lt;br /&gt;
## File browsing&lt;br /&gt;
## File linking (editor integration)&lt;br /&gt;
## Upload from repository&lt;br /&gt;
&lt;br /&gt;
==File serving API==&lt;br /&gt;
Deals with serving of files - browser requests file, Moodle sends it back. We have three main files. It is important to setup slasharguments on server (file.php/some/thing/xxx.jpg), any content that relies on relative links can not work without it (scorm, uploaded html pages, etc.).&lt;br /&gt;
&lt;br /&gt;
===file.php===&lt;br /&gt;
Serves course files.&lt;br /&gt;
&lt;br /&gt;
Implements basic file access. Ideally only images and files linked from course sections should be there, no XSS protection required - we expect javascript, sw, etc. there, no way to make it &amp;quot;secure&amp;quot;. The access control is not critical any more if we move most of the files into modules&lt;br /&gt;
&lt;br /&gt;
The file name and parameter structure is critical for backwards compatibility of existing course content.&lt;br /&gt;
&lt;br /&gt;
 /file.php/courseid/dir/dir/filename.ext&lt;br /&gt;
&lt;br /&gt;
Internally the files would be stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;$coursecontextid, &#039;filearea&#039;=&amp;gt;&#039;content&#039;, &#039;itemid&#039;=&amp;gt;0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===pluginfile.php===&lt;br /&gt;
(aka modfile.php)&lt;br /&gt;
Sends module, block, question files.&lt;br /&gt;
* modules decide about access control&lt;br /&gt;
* optional XSS protection - student submitted files must not be served with normal headers, we have to force download instead; ideally there should be second wwwroot for serving of untrusted files&lt;br /&gt;
* only internal links to selected areas are supported - you can link images in summary area, but not the assignment submissions&lt;br /&gt;
&lt;br /&gt;
Absolute file links need to be rewritten if html editing allowed in module. The links are stored internally as relative links. Before editing or display the internal link representation is converted to absolute links using simple str_replace() @@thipluginlink/summary@@/image.jpg --&amp;gt; /pluginfile.php/assignmentcontextid/summary/image.jpg, it is converted back to internal links before saving.&lt;br /&gt;
&lt;br /&gt;
::Can the distinct file areas supported by one plugin be declared somehow in order add some information about them? For example, I think it can be interesting to declare:&lt;br /&gt;
::* assignment_summary:&lt;br /&gt;
::** relpath=&#039;summary&#039;&lt;br /&gt;
::** userdata=false&lt;br /&gt;
::** anotherproperty=anothervalue&lt;br /&gt;
::* assignment_submission:&lt;br /&gt;
::** relpath=&#039;submission/@@USERID@@&#039;&lt;br /&gt;
::** userdata=false&lt;br /&gt;
::** anotherproperty=anothervalue&lt;br /&gt;
::* and so on...&lt;br /&gt;
::And then, when the editor &amp;quot;receives&amp;quot; one &amp;quot;assignment_summary&amp;quot; areaname, if knows what to show and so on? Also that info could be useful to know, in backup &amp;amp; restore if some fileareas have to be processed or no (userdata=false). Or also, when reconstructing the links (str_replace() above). And will cause to have a well defined list of fileareas by module, instead of coding them in a free way (prone to errors). [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 16:35, 28 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
::Something like this will be part of file management API, hardcoding this in file storage would make it less flexible imo [[User:Skodak|Skodak]]&lt;br /&gt;
&lt;br /&gt;
::Yup, yup. Storage doesn&#039;t know anything but get/put files (nothing else). It&#039;s part of management, absolutely. [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 11:21, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/contextid/areaname/arbitrary/params/or/dirs/filename.ext&lt;br /&gt;
&lt;br /&gt;
pluginfile.php detects the type of plugin from context table, fetches basic info (like $course or $cm if appropriate) and calls plugin function (or later method) which does the access control and finally sends the file to user. &#039;&#039;areaname&#039;&#039; separates files by type and divides the context into several subtrees - for example &#039;&#039;summary&#039;&#039; files (images used in module intros), post attachments, etc.&lt;br /&gt;
&lt;br /&gt;
====assignment example====&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/summary/someimage.jpg&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/submission/submissionid/attachmentname.ext&lt;br /&gt;
 /pluginfile.php/assignmentcontextid/extra/allsubmissionfiles.zip&lt;br /&gt;
&lt;br /&gt;
::Uhm... all those files together? What&#039;s going to differentiate the &amp;quot;submission&amp;quot; path in the example above from the &amp;quot;summary&amp;quot; path? Is it supposed that the editor, or the filemanager won&#039;t allow , for example to pick-up one file from the &amp;quot;submission&amp;quot; area to be used in the summary of one assignment and only the &amp;quot;summary&amp;quot; area will be showed? That means multiple file managers by context and it&#039;s against the clean &amp;quot;one file manager per context&amp;quot; agreed below [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 21:28, 26 June 2008 (CDT)&lt;br /&gt;
::Yes Eloy, the different areas (summary, submission) etc. have different uses, different access control. There are two types of file manager - the two pane file manager which lists all contexts+areas user may access, and minimalistic manager in html editor which shows only subset of areas from current plugin (because you can not link anything else).&lt;br /&gt;
&lt;br /&gt;
====scorm example====&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/scormcontextid/summary/someimage.jpg&lt;br /&gt;
 /pluginfile.php/scormcontextid/content/revisionnumber/dir/somescormfile.js&lt;br /&gt;
&lt;br /&gt;
The revision counter is incremented when any file changes in order to prevent caching problems. The lifetime should be adjustable in module settings.&lt;br /&gt;
&lt;br /&gt;
====quiz example====&lt;br /&gt;
&lt;br /&gt;
 pluginfile.php/quizcontextid/summary/niceimage.jpg&lt;br /&gt;
 pluginfile.php/quizcontextid/report/type/export.ods&lt;br /&gt;
&lt;br /&gt;
====questions example====&lt;br /&gt;
&lt;br /&gt;
 pluginfile.php/SYSCONTEXTID/question/questionid/file.jpg&lt;br /&gt;
&lt;br /&gt;
====blog example====&lt;br /&gt;
Blog entries or notes in general do not have context id (because they live in system context, SYSCONTEXTID below is the id of system context).&lt;br /&gt;
The note attachments are always served with XSS protection on, ideally we should use separate wwwroot for this. Access control can be hardcoded.&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/SYSCONTEXTID/blog/blogentryid/attachmentname.ext&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;SYSCONTEXTID, &#039;filearea&#039;=&amp;gt;&#039;blog&#039;, &#039;itemid&#039;=&amp;gt;$blogentryid)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
====backup example====&lt;br /&gt;
It would be nice to have some special protection of backup files - new capabilities for backup file download, upload. Backups contain a lot of personal info, we could block restoring of backups from other sites too.&lt;br /&gt;
&lt;br /&gt;
 /pluginfile.php/coursecontextid/backup/backupfile.zip&lt;br /&gt;
&lt;br /&gt;
Internally stored in &amp;lt;code&amp;gt;array(&#039;contextid&#039;=&amp;gt;$coursecontextid, &#039;filearea&#039;=&amp;gt;&#039;backup&#039;, &#039;itemid&#039;=&amp;gt;0)&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===userfile.php===&lt;br /&gt;
Personal file storage, intended as an online storage of work in progress like assignments before the submission.&lt;br /&gt;
* read/write own files only for now&lt;br /&gt;
* option to share with others later&lt;br /&gt;
* personal &amp;quot;websites&amp;quot; will not be supported (security)&lt;br /&gt;
&lt;br /&gt;
 /userfile.php/userid/dir/dir/filename.ext&lt;br /&gt;
&lt;br /&gt;
===rssfile.php===&lt;br /&gt;
Replaces rss/file.php which is kept only for backwards compatibility.&lt;br /&gt;
RSS files should not require sessions/cookies, URLs should contain some sort of security token/key.&lt;br /&gt;
Internally the files may be stored in database or together with other files.&lt;br /&gt;
Performance improvements - we should support both Etag (cool) and Last-Modified (more used), when we receive If-None-Match/If-Modified-Since =&amp;gt; 304 &lt;br /&gt;
&lt;br /&gt;
 /rssfile.php/contextid/any/parameters/module/wants/rss.xml&lt;br /&gt;
 /rssfile.php/SYSCONTEXTID/blog/userid/rss.xml&lt;br /&gt;
&lt;br /&gt;
Again modules and plugins decide what gets sent to user.&lt;br /&gt;
&lt;br /&gt;
===Temporary files===&lt;br /&gt;
Temporary files are usually used during the lifetime of one script only.&lt;br /&gt;
uses:&lt;br /&gt;
* exports&lt;br /&gt;
* imports&lt;br /&gt;
* zipping/unzipping&lt;br /&gt;
* processing by executable files (latex, mimetex)&lt;br /&gt;
&lt;br /&gt;
Ideally these files should never use utf-8 (which is a major problem for zipping at the moment).&lt;br /&gt;
Proposed new sha1 based file storage is not suitable both for performance and technical reasons.&lt;br /&gt;
&lt;br /&gt;
===Legacy file storage and serving===&lt;br /&gt;
Going to use good-old separate directories in $CFG-&amp;gt;dataroot.&lt;br /&gt;
&lt;br /&gt;
file serving and storage:&lt;br /&gt;
# user avatars - user/pix.php&lt;br /&gt;
# group avatars - user/pixgroup.php&lt;br /&gt;
# tex, algebra - filter/tex/* and filter/algebra/*&lt;br /&gt;
# rss cache (?full rss rewrite soon?) - backwards compatibility only rss/file.php&lt;br /&gt;
&lt;br /&gt;
only storage:&lt;br /&gt;
#sessions&lt;br /&gt;
&lt;br /&gt;
==File storage API==&lt;br /&gt;
Modules in general work only with local Moodle files. One of the major reason is performance when accessing external repository files. It will be possible to use repositories instead of file uploading and also to keep local files synced with external repository.&lt;br /&gt;
&lt;br /&gt;
File contents are stored in moodledata/filepool indexed using SHA1 hashes instead of file names; file names, relative paths and other metadata will be stored in file(_xxx) database tables. This should be fully abstracted so that modules do not actually know where the files are located. When storing files the content is sent as string or file handle, when reading content it is returned as file handle.&lt;br /&gt;
&lt;br /&gt;
===files table===&lt;br /&gt;
&lt;br /&gt;
This table contains one entry for every file.  Enough information is kept here so that the file can be fully identified and retrieved again if necessary.&lt;br /&gt;
&lt;br /&gt;
note: plural used because file is a reserved word&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|sha1hash&lt;br /&gt;
|varchar(40)&lt;br /&gt;
| &lt;br /&gt;
|The sha1 hash of content.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;contextid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|The context id defined in context table - identifies the instance of plugin owning the file.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filearea&lt;br /&gt;
|varchar(50)&lt;br /&gt;
|&lt;br /&gt;
|Like &amp;quot;submissions&amp;quot;, &amp;quot;intro&amp;quot; and &amp;quot;content&amp;quot; (images and swf linked from summaries), etc.; &amp;quot;blogs&amp;quot; and &amp;quot;userfiles&amp;quot; are special case that live at the system context.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|itemid&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|Some plugin specific item id (eg. forum post, blog entry or assignment submission or user id for user files)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filepath&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|relative path to file from module content root, useful in Scorm and Resource mod - most of the mods do not need this&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filename&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The full Unicode name of this file (case sensitive)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|filesize&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|size of file - bytes&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|mimetype&lt;br /&gt;
|varchar(100)&lt;br /&gt;
|NULL&lt;br /&gt;
|type of file&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;userid&#039;&#039;&#039;&lt;br /&gt;
|int(10)  &lt;br /&gt;
|NULL&lt;br /&gt;
|Optional - general user id field - meaning depending on plugin&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timecreated&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The time this file was created&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timemodified&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The last time the file was modified&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
index on &amp;quot;contextid, filearea, itemid&amp;quot; and &amp;quot;sha1hash&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Plugin type is not specified because it is derived from contextid, items like blog that do not have own context will use own filearea usually from systemcontextid.&lt;br /&gt;
&lt;br /&gt;
::Perhpas we could also hash filepath and filename and index by them, to save some text limitations in the DB side (length limits of indexes, not indexable, complex retrieval...). [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 11:54, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
::Also, perhaps we should store finally the plugin type there to save some queries per request, using it to drive to the correct file handling of each plugin. [[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 18:54, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
=== files_cleanup table ===&lt;br /&gt;
&lt;br /&gt;
This table contains candidates for deletion from the file pool. Files are not deleted immediately, cron uses the files_cleanup table, verifies the file is not used any more and deletes it from pool. Reasons for cron clean-up are performance and prevention of collision - there could be a problem with concurrent uploads and deletes, we will probably need to add some table-based locking during the clean-up.&lt;br /&gt;
&lt;br /&gt;
We might add an extra script that does deep validation of pool area - report missing files, report orphaned files, content not matching the sha1 filename, etc. - this would very very time consuming.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|sha1hash&lt;br /&gt;
|varchar(40)&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
=== files_metadata table ===&lt;br /&gt;
&lt;br /&gt;
This table contains extra metadata about files.  Repositories could provide this, or it could be manually edited in the local copy.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;fileid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|Id of file.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;name&#039;&#039;&#039;&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The name of extra metadata&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|value&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|Value&lt;br /&gt;
&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===files_acl table===&lt;br /&gt;
&lt;br /&gt;
This table describes optional ACL for file. This is not required in majority of cases, modules usually hardcode the file access logic, course files should not be used much any more.&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;fileid&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
| &lt;br /&gt;
|The file we are defining access for&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;contextid&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The context where this file is being published&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;capability&#039;&#039;&#039;&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|The capability that is required to see this file.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
====acl notes====&lt;br /&gt;
* this is missing some concept similar to &#039;&#039;&#039;user/group/others&#039;&#039;&#039;, for example in case of user files typical user can not assign permissions or view them - this becomes useless there&lt;br /&gt;
* it is more important to synchronise the availability of file link and the file itself - having link pointing to inaccessible file or file which is accessible when not wanted are both problems&lt;br /&gt;
* browser/proxy caching works against us here - &amp;quot;secret&amp;quot; files should not be cached&lt;br /&gt;
&lt;br /&gt;
===files_sync table===&lt;br /&gt;
&lt;br /&gt;
This table contains information on how to synchronise data with repositories. Data would be synchronised from cron.php or on demand from file manager. The sync would be one way only (repository--&amp;gt;local file).&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; cellpadding=&amp;quot;2&amp;quot; cellspacing=&amp;quot;0&amp;quot;&lt;br /&gt;
|&#039;&#039;&#039;Field&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Type&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;fileid&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|Id of file.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;repositoryid&#039;&#039;&#039;&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The repository instance this is associated with, see [[Development:Repository_API]]&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|updates&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|Specifies the update schedule (0 = none, 1 = on demand, other = some period in seconds)&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|repositorypath&lt;br /&gt;
|text&lt;br /&gt;
|&lt;br /&gt;
|The full path to the original file on the repository&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timeimportfirst&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The first time this file was imported into Moodle&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|timeimportlast&lt;br /&gt;
|int(10)&lt;br /&gt;
|&lt;br /&gt;
|The most recent time that this file was imported into Moodle&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
===File content storage===&lt;br /&gt;
Originally the file storage hierarchy contained a lot of metadata including userids, entry ids, filenames, etc. The file content will now be stored separately from file metadata. It must supports utf8 on all platforms.&lt;br /&gt;
&lt;br /&gt;
File storing:&lt;br /&gt;
# calculate SHA1 hash of content&lt;br /&gt;
# check if file with SHA1 name exists, if not add the file to file pool&lt;br /&gt;
# remove SHA1 from list of deleted files if found there&lt;br /&gt;
# store file in &#039;&#039;file&#039;&#039; table, use SHA1 as file pool identifier&lt;br /&gt;
&lt;br /&gt;
File reading:&lt;br /&gt;
#fetch file record from &#039;file&#039; table - probably using file id or combination of contextid+instanceid&lt;br /&gt;
#fetch content of file &lt;br /&gt;
&lt;br /&gt;
File deleting:&lt;br /&gt;
#delete record from &#039;&#039;file&#039;&#039; table, remember file SHA1&lt;br /&gt;
#store the deleted SHA1 in deleted files table, do not remove the physical file yet&lt;br /&gt;
#wait for cron cleanup script to actually delete the file named SHA1 (proper table locking needed to prevent race conditions when adding/deleting files)&lt;br /&gt;
&lt;br /&gt;
====File pool details====&lt;br /&gt;
located in $CFG-&amp;gt;dataroot/filepool/, all files can not be stored in one directory due to OS limitations, it uses 3 levels based on first three characters of sha1 hash. It is unlikely that there will be thousands of files with the same first 3 chars in sha1 hash of their content.&lt;br /&gt;
&lt;br /&gt;
This type of storage saves a lot of disk space when storing multiple copies of the same large file. It can also help substantially when synchronising data with external repositories. Another benefit is we can detect inconsistencies in file content.&lt;br /&gt;
&lt;br /&gt;
File read performance is similar to previous code, file write performance will be slower - due to hashing and extra database access.&lt;br /&gt;
&lt;br /&gt;
 dataroot &lt;br /&gt;
    /filepool&lt;br /&gt;
       /00&lt;br /&gt;
       /01&lt;br /&gt;
       ...&lt;br /&gt;
       /&#039;&#039;&#039;23&#039;&#039;&#039;&lt;br /&gt;
         /00&lt;br /&gt;
         /01&lt;br /&gt;
         ...&lt;br /&gt;
         /&#039;&#039;&#039;1e&#039;&#039;&#039;&lt;br /&gt;
            /00&lt;br /&gt;
            /01&lt;br /&gt;
            ...&lt;br /&gt;
            /&#039;&#039;&#039;2d&#039;&#039;&#039;&lt;br /&gt;
               /231e2dc421be4fcd0172e5afceea3970e2f3d940.jpg&lt;br /&gt;
       ...&lt;br /&gt;
       /fe&lt;br /&gt;
       /ff&lt;br /&gt;
&lt;br /&gt;
==File management API==&lt;br /&gt;
&lt;br /&gt;
This section describes following:&lt;br /&gt;
#file manager&lt;br /&gt;
#integration with html editor&lt;br /&gt;
#interactions with repos&lt;br /&gt;
&lt;br /&gt;
===File manager===&lt;br /&gt;
Single pane file manager is hard to implement without drag &amp;amp; drop which is notoriously problematic in web based applications. I propose to implement a two pane commander-style file manager. Two pane manager allows you to easily copy/move files between two different contexts (ex: courses).&lt;br /&gt;
&lt;br /&gt;
File manager must not interact directly with filesystem API, instead each module should return traversable tree of files and directories with both real and localised names (localised names are needed for dirs like backupdata).&lt;br /&gt;
&lt;br /&gt;
Originally there was a single file tree for each course. We need to fully separate each module/block from the course files and there might be also independent file areas in modules (ex: module introduction, content files, submissions, post attachments). File area may be defined as a small tree where we can use relative paths. These file areas are hanging from the branches of the context tree (this needs a picture).&lt;br /&gt;
&lt;br /&gt;
===Integration with htmleditor===&lt;br /&gt;
Html editor should be able to browse only relevant files - for example when editing resource introduction only images from the file area of that resource should be available; when editing html resource page only the content area images should be listed.&lt;br /&gt;
&lt;br /&gt;
There are several problems here:&lt;br /&gt;
# when adding new resource its context does not exist yet, we will have to create some table to handle temporary file storage for adding of new stuff, not easy but should be solvable - maybe we could abuse the course context id or store it temporarily in some special user file area&lt;br /&gt;
# we can not use absolute address relinking for pluginfile.php links, instead we can use the absolute links only when editing and before storage convert them to something like @@thispluginfile/intro@@/2112/112/image.jpg before storage. the local links would be converted to full absolute links before display or editing. Not all file areas will support this (ex: linking to assignment submission does not make sense because nobody else may access it anyway). This would allow us to implement image preview in html editor.&lt;br /&gt;
&lt;br /&gt;
Html editor should contain simplified single pane file manager with basic operations only - select file area, browse file area, upload file/copy user file/use repo file, delete. The editor will communicate with modules and core through ajax call to some script specified by module embedding the editor. The callback script would use different logic to construct the tree of files than the File manager, it needs to know only about files that other ppl viewing the resulting html may access.&lt;br /&gt;
&lt;br /&gt;
===Interactions with repos===&lt;br /&gt;
Repositories may serve as a replacement for file uploading. They may be also used to synchronise files between courses. The repo option should be available whenever there is a file upload field, sometimes with extra &amp;quot;keep synchronised&amp;quot; option (this would not make sense for stuff like assignment submissions).&lt;br /&gt;
&lt;br /&gt;
==Upgrade, migration and backwards compatibility==&lt;br /&gt;
It is going to be a pain again like DML/DDL ;-)&lt;br /&gt;
&lt;br /&gt;
===Code backwards compatibility===&lt;br /&gt;
0% backwards compatibility related to file storage. New objects will be mandatory to use. Old $CFG-&amp;gt;dataroot/$courseid/ will be empty, $CFG-&amp;gt;dataroot/blog/ too, etc.&lt;br /&gt;
&lt;br /&gt;
===Content backwards compatibility===&lt;br /&gt;
Means existing courses should not loose images, flash, etc. Though some new features (like resource sharing - if implemented) may not work with existing data that still uses files from course files area.&lt;br /&gt;
&lt;br /&gt;
There might be a breakage of links due to special characters stripping in uploaded files which will not match the links in uploaded html files any more. This should not be very common I hope.&lt;br /&gt;
&lt;br /&gt;
===Migration of content===&lt;br /&gt;
* resources - move files to new resource content file area; can be done automatically for pdf, image resources; definitely not accurate for uploaded web pages&lt;br /&gt;
* questions - image file moved to new are, image tag appended to questions&lt;br /&gt;
* moddata files - the easiest part, just move to new storage&lt;br /&gt;
* coursefiles - there might be a lot of outdated files :-( :-(&lt;br /&gt;
* rss feeds links in readers - will be broken, the new security related code would break it anyway&lt;br /&gt;
&lt;br /&gt;
===Moving files to files table and file pool===&lt;br /&gt;
The migration process must be interruptable because it might take a very long time. The files would be moved from old location, the restarting would be straightforward.&lt;br /&gt;
Proposed stages:&lt;br /&gt;
#migration of all course files except moddata - finish marked by some $CFG-&amp;gt;files_migrated=true; - this step breaks the old file manager and html editor integration&lt;br /&gt;
#migration of blog attachments&lt;br /&gt;
#migration of question files&lt;br /&gt;
#migration of moddata files - each module is responsible to copy data from converted coursefiles or directly from moddata which is not converted automatically&lt;br /&gt;
&lt;br /&gt;
Some ppl use symbolic links in coursefiles - we must make sure that those will be copied to new storage in both places, though they can not be linked any more - anybody wanting to have content synced will need to move the files to some repository and set up the sync again.&lt;br /&gt;
&lt;br /&gt;
::Talked about a double task here, when migrating course files to module areas:&lt;br /&gt;
::# Parse html files to detect all the dependencies and move them together.&lt;br /&gt;
::# Fallback in pluginfile.php so, if something isn&#039;t found in module filearea, search for it in course filearea, copying it and finally, serving it.&lt;br /&gt;
&lt;br /&gt;
:: Also we talked about the possibility of add a new setting to resource in order to define if it should work against old coursefiles or new autocontained file areas. Migrated resources will point to old coursefiles while new ones will enforce autocontained file areas.&lt;br /&gt;
&lt;br /&gt;
:: it seems that only resource files will be really complex (because allow arbitrary HTML inclusion). The rest (labels, intros... doesn&#039;t) and should be easier to parse.&lt;br /&gt;
&lt;br /&gt;
::[[User:Eloy Lafuente (stronk7)|Eloy Lafuente (stronk7)]] 19:00, 29 June 2008 (CDT)&lt;br /&gt;
&lt;br /&gt;
==Backup/restore changes==&lt;br /&gt;
File handling in backups needs to be fully rewritten - list of files in xml + pool of sha1 named files with contents. This solves the utf-8 trouble here, yay!!&lt;br /&gt;
&lt;br /&gt;
==Quotas==&lt;br /&gt;
File size will be stored in files table, we can use simple queries to find out how much space is used, however this may not be accurate because the sha1 hash based storage eliminates duplicate files.&lt;br /&gt;
*total course files - find out all contexts used in course, query files table with contextid IN ($listofcontexts)&lt;br /&gt;
*module files - find module context and calculate space per file area&lt;br /&gt;
*user files quota - inside the personal area only, counting all attachments in all mods might take a while&lt;br /&gt;
We could also divide the file size by number of instances that are using it, this might be considered more accurate in some scenarios.&lt;br /&gt;
&lt;br /&gt;
==Other==&lt;br /&gt;
*antivirus scanning + upload manager rewrite/integration with forms lib&lt;br /&gt;
*zip compression and extraction&lt;br /&gt;
&lt;br /&gt;
==Major problems==&lt;br /&gt;
List of hard to solve prolbems&lt;br /&gt;
&lt;br /&gt;
===unicode zip support===&lt;br /&gt;
Unicode chars in zip files uploaded by teachers - unfortunately there is no 100% solution that will work for anybody because most zip programs do not support unicode, it is usually &#039;&#039;garbage in/garbage out&#039;&#039; which works in some cases only&lt;br /&gt;
&lt;br /&gt;
Latest WinZIP 11.2 and Total Commander seem to support some very limited form of utf-8 encodings of file names. I managed to create a zip file in Windows (Czech locale) and extract them in linux with native PHP zip functions, the only step I needed to add was conversion cp852(DOS charset for Czech locale) -&amp;gt; UTF-8. The native windows zipping did not work for me though, because it does some different borking of charsets.&lt;br /&gt;
&lt;br /&gt;
In any case it seems likely that native PHP support in PHP 5.2.x should be better than current pclzip or infozip binary.&lt;br /&gt;
&lt;br /&gt;
===empty directories===&lt;br /&gt;
Hmm, thinking a bit more about Justin&#039;s comment I realised there is no support for empty directories in this proposal. This will require either new table or some hack in files table - maybe we could add files with &amp;quot;.&amp;quot; as name and just skip them when iterating directory content.&lt;br /&gt;
&lt;br /&gt;
===file overwriting===&lt;br /&gt;
Concept of file overwriting does not exist anymore here, the path+filename are not enforced to be unique - we can not make index because sloppy mssql does not allow indexes larger than 900 bytes :-( We will haev to emulate it somehow and deal with collisions if found.&lt;br /&gt;
&lt;br /&gt;
== Some little comments to be considered (to avoid forgetting them) ==&lt;br /&gt;
&lt;br /&gt;
* each context will have its own &amp;quot;file manager&amp;quot;&lt;br /&gt;
* separate &amp;quot;file manager context&amp;quot; files (FMF) and &amp;quot;internal context&amp;quot; (ICF) files (current modedit files, submissions, attachements...)&lt;br /&gt;
* /pluginfile.php/SYSCONTEXTID/{blog|question} and so... will have own FMF too? Or only ICF ?&lt;br /&gt;
* Way to copy between contexts&lt;br /&gt;
* Links = -1 for them&lt;br /&gt;
* Deletion strategy (locks, quarantine status...)&lt;br /&gt;
* include support for quotas per user, per course, etc &lt;br /&gt;
* upgrade process should be interruptable (like the unicode upgrade) so it can be stopped/restarted any time&lt;br /&gt;
&lt;br /&gt;
==Justin&#039;s thinking out loud==&lt;br /&gt;
&lt;br /&gt;
I&#039;m actually working on implementing this along with extending an existing Alfresco integration to work together with the whole File / Repository system and I wanted to get some of my comments and thoughts in here for feedback.  Go easy on me.  =)&lt;br /&gt;
&lt;br /&gt;
So far I&#039;ve only got one that I&#039;d like to solicit some feedback on (BTW, if this would be better suited to a forum discussion, let me know):&lt;br /&gt;
&lt;br /&gt;
===Not storing the full &#039;&#039;filepath&#039;&#039; with each entry in the &#039;&#039;&#039;file&#039;&#039;&#039; table===&lt;br /&gt;
*For browsing a directory structure, determining things like child directories or a parent directory given a filepath requires a lot of extraneous coding in PHP.  I think it might be better served to create a new &#039;&#039;&#039;file_directory&#039;&#039;&#039; table, storing only a directory name, and reference to a parent directory record.  The benefits here are that we&#039;re storing a lot of duplicate text field values in the &#039;&#039;&#039;file&#039;&#039;&#039; table and browsing through the file picker for local files doesn&#039;t require a lot of PHP overhead to calculate links to parent / child directories.&lt;br /&gt;
*Given that file permissions are no longer calculated using structured file paths, using the complete, full, path to a given file would most likely never be needed.&lt;br /&gt;
&lt;br /&gt;
*The &#039;&#039;&#039;repositorypath&#039;&#039;&#039; field in the &#039;&#039;&#039;repository_sync&#039;&#039;&#039; table still makes sense, though.&lt;br /&gt;
&lt;br /&gt;
*The &#039;&#039;&#039;file_directory&#039;&#039;&#039; 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;Default&#039;&#039;&#039; &lt;br /&gt;
|&#039;&#039;&#039;Info&#039;&#039;&#039; &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;id&#039;&#039;&#039; &lt;br /&gt;
|int(10)  &lt;br /&gt;
|&lt;br /&gt;
|autoincrementing &lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;parent&#039;&#039;&#039; &lt;br /&gt;
|int(10)&lt;br /&gt;
| &lt;br /&gt;
|ID of directory that this record is a child of.&lt;br /&gt;
&lt;br /&gt;
|-&lt;br /&gt;
|&#039;&#039;&#039;directoryname&#039;&#039;&#039;&lt;br /&gt;
|varchar(255)&lt;br /&gt;
|&lt;br /&gt;
|The actual name of this directory.&lt;br /&gt;
|}&lt;br /&gt;
&lt;br /&gt;
skodak: filepath is stored in files table - its root is the corresponding filearea, the file manager will use the context tree to find all plugins/courses and ask them to return the list of areas with all those small branches inside it&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[Development:Repository API]]&lt;br /&gt;
* [[Development:Portfolio API]]&lt;/div&gt;</summary>
		<author><name>Nicolasconnault</name></author>
	</entry>
</feed>