<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://docs.moodle.org/dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Rwijaya</id>
	<title>MoodleDocs - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="https://docs.moodle.org/dev/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Rwijaya"/>
	<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/Special:Contributions/Rwijaya"/>
	<updated>2026-10-04T12:25:15Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.43.5</generator>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42834</id>
		<title>HTML Guidelines</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42834"/>
		<updated>2013-11-01T09:11:42Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: /* Header */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Purpose==&lt;br /&gt;
The purpose of these HTML Guidelines it to provide the groundwork for a clear and consistent Document Object Model throughout Moodle, that allows for semantic definition of the content, in keeping with best practices from across the web. As a side affect, this should improve the ability to theme Moodle in a more consistent manner, and aid in improving the accessibility of Moodle&#039;s pages in general.&lt;br /&gt;
&lt;br /&gt;
==Header==&lt;br /&gt;
Header usage should be hierarchical within page views&lt;br /&gt;
Reference link: [[Standardize classnames and layout to facilitate theming]]&lt;br /&gt;
&lt;br /&gt;
* Headers should be hierarchical, with two or more &amp;amp;lt;h3&amp;amp;gt; under each &amp;amp;lt;h2&amp;amp;gt; and so forth. However an exception applied to front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed for displaying the page&#039;s items (eg: list of courses, list of categories, etc).&lt;br /&gt;
* &amp;amp;lt;h2&amp;amp;gt; is the top-level header of the page content/central column and should only be used once within a page. However, there are couple of exceptions applied to level 2 heading:&lt;br /&gt;
** Front-page is allowed to have multiple &amp;amp;lt;h2&amp;amp;gt;&lt;br /&gt;
** External Tools (LTI) activity has setting to display the header. The setting name to display heading is &#039;Display activity name when launched&#039; and the default value is set to true.  If user disabled this setting, the header will not available on the page.&lt;br /&gt;
* Headers should not be used for notification. Notification classes should be used for notification.&lt;br /&gt;
* For H1 and H2 elements, no additional classes should be added. For H3 and below, some classes that describe the purpose of that heading such as sectionname, and others that provide functionality, such as dimmed_text, may be needed. Blanket and vague classnames should be eliminated (main, header, title, mdl-align, etc).&lt;br /&gt;
* We shouldn&#039;t be wrapping a heading in a div just to allow it to be styled. This complicates the DOM, and defeats the purpose of any effort to standardize the appearance of headings across the page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Not this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;These is your score on XX activity.&amp;lt;/h2&amp;gt;&lt;br /&gt;
   &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Generic submit button&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;This paragraph gives additional information and explains what&#039;s to come.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;notifyproblem&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Block===&lt;br /&gt;
Blocks should consist of the following:&lt;br /&gt;
* Block&#039;s name should start at level 2&lt;br /&gt;
* Block&#039;s alignment should used the definition in theme, unless it is forcely modified through Tinymce which is using inline styling.&lt;br /&gt;
[[File:block.png]]&lt;br /&gt;
&lt;br /&gt;
==Activity page==&lt;br /&gt;
The display for the activity&#039;s content should contained the following:&lt;br /&gt;
* Course name with level 1 header&lt;br /&gt;
* Activity name with level 2 header&lt;br /&gt;
* Content header should start at level 3 and lower for its sub-content.  Although Tinymce allowed the use of level 1 and 2, it should be avoided.&lt;br /&gt;
* Use theme alignment for displaying the content.&lt;br /&gt;
* Content styling such as: alignment, font-size, font-family should inherit from theme.&lt;br /&gt;
[[File:activity.png]]&lt;br /&gt;
&lt;br /&gt;
==Update in core API:==&lt;br /&gt;
As part of removing &#039;.main&#039; class from header tag, we have updated the following functions in core outputrenderer API file:&lt;br /&gt;
===Heading===&lt;br /&gt;
For this function we changed the default value for the classes parameter value from &#039;main&#039; to null.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
heading($text, $level = 2, $classes = &#039;main&#039;, $id = null)&lt;br /&gt;
To&lt;br /&gt;
heading($text, $level = 2, $classes = null, $id = null)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Heading with help===&lt;br /&gt;
For this function we added two additional parameters to set the heading level and classname.  The level parameter value is defaulted to 2 and classnames is defaulted to null.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
heading_with_help($text, $helpidentifier, $component = &#039;moodle&#039;, $icon = &#039;&#039;, $iconalt = &#039;&#039;)&lt;br /&gt;
To&lt;br /&gt;
heading_with_help($text, $helpidentifier, $component = &#039;moodle&#039;, $icon = &#039;&#039;, $iconalt = &#039;&#039;, $level = 2, $classnames = null)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The detailed of changes for the above functions is reported in MDL-41438.&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42328</id>
		<title>HTML Guidelines</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42328"/>
		<updated>2013-09-13T06:06:12Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Header==&lt;br /&gt;
Header usage should be hierarchical within page views&lt;br /&gt;
Reference link: [[Standardize classnames and layout to facilitate theming]]&lt;br /&gt;
&lt;br /&gt;
* Headers should be hierarchical, with two or more &amp;amp;lt;h3&amp;amp;gt; under each &amp;amp;lt;h2&amp;amp;gt; and so forth. However an exception applied to front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed for displaying the page&#039;s items (eg: list of courses, list of categories, etc).&lt;br /&gt;
* &amp;amp;lt;h2&amp;amp;gt; is the top-level header of the page content/central column. There should be only one &amp;amp;lt;h2&amp;amp;gt; (except for front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed).&lt;br /&gt;
* Headers should not be used for notification. Notification classes should be used for notification.&lt;br /&gt;
* For H1 and H2 elements, no additional classes should be added. For H3 and below, some classes that describe the purpose of that heading such as sectionname, and others that provide functionality, such as dimmed_text, may be needed. Blanket and vague classnames should be eliminated (main, header, title, mdl-align, etc).&lt;br /&gt;
* We shouldn&#039;t be wrapping a heading in a div just to allow it to be styled. This complicates the DOM, and defeats the purpose of any effort to standardize the appearance of headings across the page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Not this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;These is your score on XX activity.&amp;lt;/h2&amp;gt;&lt;br /&gt;
   &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Generic submit button&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;This paragraph gives additional information and explains what&#039;s to come.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;notifyproblem&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Block===&lt;br /&gt;
Blocks should consist of the following:&lt;br /&gt;
* Block&#039;s name should start at level 2&lt;br /&gt;
* Block&#039;s alignment should used the definition in theme, unless it is forcely modified through Tinymce which is using inline styling.&lt;br /&gt;
[[File:block.png]]&lt;br /&gt;
&lt;br /&gt;
==Activity page==&lt;br /&gt;
The display for the activity&#039;s content should contained the following:&lt;br /&gt;
* Course name with level 1 header&lt;br /&gt;
* Activity name with level 2 header&lt;br /&gt;
* Content header should start at level 3 and lower for its sub-content.  Although Tinymce allowed the use of level 1 and 2, it should be avoided.&lt;br /&gt;
* Use theme alignment for displaying the content.&lt;br /&gt;
* Content styling such as: alignment, font-size, font-family should inherit from theme.&lt;br /&gt;
[[File:activity.png]]&lt;br /&gt;
&lt;br /&gt;
==Update in core API:==&lt;br /&gt;
As part of removing &#039;.main&#039; class from header tag, we have updated the following functions in core outputrenderer API file:&lt;br /&gt;
===Heading===&lt;br /&gt;
For this function we changed the default value for the classes parameter value from &#039;main&#039; to null.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
heading($text, $level = 2, $classes = &#039;main&#039;, $id = null)&lt;br /&gt;
To&lt;br /&gt;
heading($text, $level = 2, $classes = null, $id = null)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Heading with help===&lt;br /&gt;
For this function we added two additional parameters to set the heading level and classname.  The level parameter value is defaulted to 2 and classnames is defaulted to null.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
heading_with_help($text, $helpidentifier, $component = &#039;moodle&#039;, $icon = &#039;&#039;, $iconalt = &#039;&#039;)&lt;br /&gt;
To&lt;br /&gt;
heading_with_help($text, $helpidentifier, $component = &#039;moodle&#039;, $icon = &#039;&#039;, $iconalt = &#039;&#039;, $level = 2, $classnames = null)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The detailed of changes for the above functions is reported in MDL-41438.&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:block.png&amp;diff=42327</id>
		<title>File:block.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:block.png&amp;diff=42327"/>
		<updated>2013-09-13T02:39:32Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: Rwijaya uploaded a new version of &amp;amp;quot;File:block.png&amp;amp;quot;: Reverted to version as of 02:36, 13 September 2013&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:block.png&amp;diff=42326</id>
		<title>File:block.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:block.png&amp;diff=42326"/>
		<updated>2013-09-13T02:38:25Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: Rwijaya uploaded a new version of &amp;amp;quot;File:block.png&amp;amp;quot;: Reverted to version as of 07:36, 10 September 2013&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:block.png&amp;diff=42325</id>
		<title>File:block.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:block.png&amp;diff=42325"/>
		<updated>2013-09-13T02:37:48Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: Rwijaya uploaded a new version of &amp;amp;quot;File:block.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:block.png&amp;diff=42324</id>
		<title>File:block.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:block.png&amp;diff=42324"/>
		<updated>2013-09-13T02:36:11Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: Rwijaya uploaded a new version of &amp;amp;quot;File:block.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42302</id>
		<title>HTML Guidelines</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42302"/>
		<updated>2013-09-10T07:37:16Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: /* Block */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Header==&lt;br /&gt;
Header usage should be hierarchical within page views&lt;br /&gt;
Reference link: [[Standardize classnames and layout to facilitate theming]]&lt;br /&gt;
&lt;br /&gt;
* Headers should be hierarchical, with two or more &amp;amp;lt;h3&amp;amp;gt; under each &amp;amp;lt;h2&amp;amp;gt; and so forth. However an exception applied to front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed for displaying the page&#039;s items (eg: list of courses, list of categories, etc).&lt;br /&gt;
* &amp;amp;lt;h2&amp;amp;gt; is the top-level header of the page content/central column. There should be only one &amp;amp;lt;h2&amp;amp;gt; (except for front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed).&lt;br /&gt;
* Headers should not be used for notification. Notification classes should be used for notification.&lt;br /&gt;
* For H1 and H2 elements, no additional classes should be added. For H3 and below, some classes that describe the purpose of that heading such as sectionname, and others that provide functionality, such as dimmed_text, may be needed. Blanket and vague classnames should be eliminated (main, header, title, mdl-align, etc).&lt;br /&gt;
* We shouldn&#039;t be wrapping a heading in a div just to allow it to be styled. This complicates the DOM, and defeats the purpose of any effort to standardize the appearance of headings across the page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Not this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;These is your score on XX activity.&amp;lt;/h2&amp;gt;&lt;br /&gt;
   &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Generic submit button&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;This paragraph gives additional information and explains what&#039;s to come.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;notifyproblem&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Block===&lt;br /&gt;
Blocks should consist of the following:&lt;br /&gt;
* Block&#039;s name should start at level 2&lt;br /&gt;
* Block&#039;s alignment should used the definition in theme, unless it is forcely modified through Tinymce which is using inline styling.&lt;br /&gt;
[[File:block.png]]&lt;br /&gt;
&lt;br /&gt;
==Activity page==&lt;br /&gt;
The display for the activity&#039;s content should contained the following:&lt;br /&gt;
* Course name with level 1 header&lt;br /&gt;
* Activity name with level 2 header&lt;br /&gt;
* Content header should start at level 3 and lower for its sub-content.  Although Tinymce allowed the use of level 1 and 2, it should be avoided.&lt;br /&gt;
* Use theme alignment for displaying the content.&lt;br /&gt;
* Content styling such as: alignment, font-size, font-family should inherit from theme.&lt;br /&gt;
[[File:activity.png]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:block.png&amp;diff=42301</id>
		<title>File:block.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:block.png&amp;diff=42301"/>
		<updated>2013-09-10T07:36:11Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42300</id>
		<title>HTML Guidelines</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42300"/>
		<updated>2013-09-10T07:32:39Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Header==&lt;br /&gt;
Header usage should be hierarchical within page views&lt;br /&gt;
Reference link: [[Standardize classnames and layout to facilitate theming]]&lt;br /&gt;
&lt;br /&gt;
* Headers should be hierarchical, with two or more &amp;amp;lt;h3&amp;amp;gt; under each &amp;amp;lt;h2&amp;amp;gt; and so forth. However an exception applied to front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed for displaying the page&#039;s items (eg: list of courses, list of categories, etc).&lt;br /&gt;
* &amp;amp;lt;h2&amp;amp;gt; is the top-level header of the page content/central column. There should be only one &amp;amp;lt;h2&amp;amp;gt; (except for front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed).&lt;br /&gt;
* Headers should not be used for notification. Notification classes should be used for notification.&lt;br /&gt;
* For H1 and H2 elements, no additional classes should be added. For H3 and below, some classes that describe the purpose of that heading such as sectionname, and others that provide functionality, such as dimmed_text, may be needed. Blanket and vague classnames should be eliminated (main, header, title, mdl-align, etc).&lt;br /&gt;
* We shouldn&#039;t be wrapping a heading in a div just to allow it to be styled. This complicates the DOM, and defeats the purpose of any effort to standardize the appearance of headings across the page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Not this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;These is your score on XX activity.&amp;lt;/h2&amp;gt;&lt;br /&gt;
   &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Generic submit button&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;This paragraph gives additional information and explains what&#039;s to come.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;notifyproblem&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Block===&lt;br /&gt;
Blocks should consist of the following:&lt;br /&gt;
* Block&#039;s name should start at level 2&lt;br /&gt;
* Block&#039;s alignment should used the definition in theme, unless it is forcely modified through Tinymce which is using inline styling.&lt;br /&gt;
&lt;br /&gt;
==Activity page==&lt;br /&gt;
The display for the activity&#039;s content should contained the following:&lt;br /&gt;
* Course name with level 1 header&lt;br /&gt;
* Activity name with level 2 header&lt;br /&gt;
* Content header should start at level 3 and lower for its sub-content.  Although Tinymce allowed the use of level 1 and 2, it should be avoided.&lt;br /&gt;
* Use theme alignment for displaying the content.&lt;br /&gt;
* Content styling such as: alignment, font-size, font-family should inherit from theme.&lt;br /&gt;
[[File:activity.png]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:activity.png&amp;diff=42299</id>
		<title>File:activity.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:activity.png&amp;diff=42299"/>
		<updated>2013-09-10T07:32:05Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: Rwijaya uploaded a new version of &amp;amp;quot;File:activity.png&amp;amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=File:activity.png&amp;diff=42298</id>
		<title>File:activity.png</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=File:activity.png&amp;diff=42298"/>
		<updated>2013-09-10T07:27:37Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42297</id>
		<title>HTML Guidelines</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42297"/>
		<updated>2013-09-10T07:26:10Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Header==&lt;br /&gt;
Header usage should be hierarchical within page views&lt;br /&gt;
Reference link: [[Standardize classnames and layout to facilitate theming]]&lt;br /&gt;
&lt;br /&gt;
* Headers should be hierarchical, with two or more &amp;amp;lt;h3&amp;amp;gt; under each &amp;amp;lt;h2&amp;amp;gt; and so forth. However an exception applied to front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed for displaying the page&#039;s items (eg: list of courses, list of categories, etc).&lt;br /&gt;
* &amp;amp;lt;h2&amp;amp;gt; is the top-level header of the page content/central column. There should be only one &amp;amp;lt;h2&amp;amp;gt; (except for front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed).&lt;br /&gt;
* Headers should not be used for notification. Notification classes should be used for notification.&lt;br /&gt;
* For H1 and H2 elements, no additional classes should be added. For H3 and below, some classes that describe the purpose of that heading such as sectionname, and others that provide functionality, such as dimmed_text, may be needed. Blanket and vague classnames should be eliminated (main, header, title, mdl-align, etc).&lt;br /&gt;
* We shouldn&#039;t be wrapping a heading in a div just to allow it to be styled. This complicates the DOM, and defeats the purpose of any effort to standardize the appearance of headings across the page.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Not this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;These is your score on XX activity.&amp;lt;/h2&amp;gt;&lt;br /&gt;
   &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Generic submit button&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;This paragraph gives additional information and explains what&#039;s to come.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;notifyproblem&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Block===&lt;br /&gt;
Blocks should consist of the following:&lt;br /&gt;
* Block&#039;s name should start at level 2&lt;br /&gt;
* Block&#039;s alignment should used the definition in theme, unless it is forcely modified through Tinymce which is using inline styling.&lt;br /&gt;
&lt;br /&gt;
==Activity page==&lt;br /&gt;
The display for the activity&#039;s content should contained the following:&lt;br /&gt;
* Course name with level 1 header&lt;br /&gt;
* Activity name with level 2 header&lt;br /&gt;
* Content header should start at level 3 and lower for its sub-content.  Although Tinymce allowed the use of level 1 and 2, it should be avoided.&lt;br /&gt;
* Use theme alignment for displaying the content.&lt;br /&gt;
* Content styling such as: alignment, font-size, font-family should inherit from theme.&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42256</id>
		<title>HTML Guidelines</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42256"/>
		<updated>2013-09-03T05:31:05Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Header==&lt;br /&gt;
Header usage should be hierarchical within page views&lt;br /&gt;
Reference link: [[Standardize classnames and layout to facilitate theming]]&lt;br /&gt;
&lt;br /&gt;
* Headers should be hierarchical, with two or more &amp;amp;lt;h3&amp;amp;gt; under each &amp;amp;lt;h2&amp;amp;gt; and so forth. However an exception applied to front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed for displaying the page&#039;s items (eg: list of courses, list of categories, etc).&lt;br /&gt;
* &amp;amp;lt;h2&amp;amp;gt; is the top-level header of the page content/central column. There should be only one &amp;amp;lt;h2&amp;amp;gt; (except for front-page where multiple &amp;amp;lt;h2&amp;amp;gt; are allowed).&lt;br /&gt;
* Headers should not be used for notification. Notification classes should be used for notification.&lt;br /&gt;
* We shouldn&#039;t even need the .main class, or any other additional classes used to alter the appearance of the header. The fact that the header is located in the central column/main content area, and is a header, should be enough. The only reason we need it is because we&#039;re recruiting headers for other purposes. Some cases, such as dimmed_text and other classes that are needed to allow for added functionality are an exception to this rule. The objective is to standardize the heading, without removing functionality for indicating an element won&#039;t be visible to students etc.&lt;br /&gt;
&lt;br /&gt;
Not this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;These is your score on XX activity.&amp;lt;/h2&amp;gt;&lt;br /&gt;
   &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Generic submit button&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;This paragraph gives additional information and explains what&#039;s to come.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;notifyproblem&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Peer_reviewing&amp;diff=42147</id>
		<title>Peer reviewing</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Peer_reviewing&amp;diff=42147"/>
		<updated>2013-08-21T08:17:57Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;These are points to consider while peer-reviewing issues. Further explanation below.&lt;br /&gt;
&lt;br /&gt;
 [] Syntax&lt;br /&gt;
 [] Whitespace&lt;br /&gt;
 [] Output&lt;br /&gt;
 [] Language&lt;br /&gt;
 [] Databases&lt;br /&gt;
 [] Testing (instructions and automated tests)&lt;br /&gt;
 [] Security&lt;br /&gt;
 [] Documentation&lt;br /&gt;
 [] Git&lt;br /&gt;
 [] Sanity check&lt;br /&gt;
&lt;br /&gt;
Acceptable check-marks are Y (for yes), N (for no) or - (for not applicable). All N check-marks should be accompanied by an explanation of the problem that still needs to be addressed.&lt;br /&gt;
&lt;br /&gt;
==Syntax==&lt;br /&gt;
To allow the community of Moodle developers to work together, conventions should be followed.&lt;br /&gt;
&lt;br /&gt;
* The code is easy to understand and, where it isn&#039;t, comments have been provided.&lt;br /&gt;
* Variables are named correctly (all lower case, no camel-case, no underscores).&lt;br /&gt;
* Functions are named correctly (all lower case, no camel-case, underscores allowed).&lt;br /&gt;
* PHP DocBlocks have been updated and adhere to [[Coding_style#Documentation_and_comments|coding style guide]].&lt;br /&gt;
* Where functions are being removed, the [[Deprecation]] process is followed.&lt;br /&gt;
* The code doesn&#039;t use [[Deprecated_functions_in_2.0|deprecated functions]].&lt;br /&gt;
* $_GET, $_POST, $_REQUEST, $_COOKIE, and $_SESSION are never used.&lt;br /&gt;
&lt;br /&gt;
See the [[Coding style]] guide for details.&lt;br /&gt;
&lt;br /&gt;
==Whitespace==&lt;br /&gt;
Unnecessary whitespace changes can cause conflicts when committing code.&lt;br /&gt;
&lt;br /&gt;
Ensure that:&lt;br /&gt;
* there are no unnecessary blank lines in the new code;&lt;br /&gt;
* blank lines do not contain spaces;&lt;br /&gt;
* there are no unnecessary changes to whitespace in other areas on the file;&lt;br /&gt;
&lt;br /&gt;
==Output==&lt;br /&gt;
Output needs to be controlled by renderers to achieve consistency and correct application of themes.&lt;br /&gt;
&lt;br /&gt;
Ensure that:&lt;br /&gt;
* output renders are used to generate output strings, including HTML tags;&lt;br /&gt;
* HTML output is valid XHTML;&lt;br /&gt;
* no inline styles have been used in HTML output (everything has to be in CSS);&lt;br /&gt;
* CSS has been added to the appropriate CSS files (base, specific area, sometimes canvas); and&lt;br /&gt;
* the code doesn&#039;t use buffered output unless absolutely necessary.&lt;br /&gt;
&lt;br /&gt;
feedback any notices (E_STRICT, etc) seen into the MDL.&lt;br /&gt;
&lt;br /&gt;
==Language==&lt;br /&gt;
To achieve appropriate internationalisation of Moodle, language strings must be managed correctly.&lt;br /&gt;
&lt;br /&gt;
Ensure that:&lt;br /&gt;
* new language strings are named correctly (all lower case, no camel-case, underscores are permissible in some cases);&lt;br /&gt;
* language strings are used instead of hard-coded strings for text output;&lt;br /&gt;
* language strings have not been removed or renamed in stable branches (permitted only in master); and&lt;br /&gt;
* AMOS commands have been specified when moving, copying, or deleting language strings.&lt;br /&gt;
&lt;br /&gt;
==Databases==&lt;br /&gt;
DB calls are the greatest performance bottleneck in Moodle.&lt;br /&gt;
&lt;br /&gt;
If there is SQL code you can test quickly then do so. &lt;br /&gt;
&lt;br /&gt;
Ensure that:&lt;br /&gt;
* there are minimal DB calls (no excessive use of the DB); and&lt;br /&gt;
* the code uses SQL compatible with all the supported DB engines (check all selected fields appear in an &#039;order by&#039; clause).&lt;br /&gt;
&lt;br /&gt;
==Testing instructions and automated tests==&lt;br /&gt;
It is the developer&#039;s responsibility to test code before integration. Issues should not be sent for peer review without tests so that the peer reviewer can assess their quality and use them to consider the scope of the issue.&lt;br /&gt;
&lt;br /&gt;
Ensure that:&lt;br /&gt;
* there are specific testing instructions that state how, as well as what, to test. Please ensure that the testing instructions are in the correct format: [https://docs.moodle.org/dev/Behat Behat gherkin];&lt;br /&gt;
* new unit tests have been added when there is a change in functionality; and&lt;br /&gt;
* &#039;&#039;&#039;unit tests pass&#039;&#039;&#039; for related areas where changes have been made.&lt;br /&gt;
* &#039;&#039;&#039;Behat tests pass&#039;&#039;&#039; for related areas where changes have been made, especially when it involved UI changes.&lt;br /&gt;
&lt;br /&gt;
==Security==&lt;br /&gt;
The user community relies on Moodle being responsibly secure.&lt;br /&gt;
&lt;br /&gt;
Ensure that:&lt;br /&gt;
* user login is checked where an identity is needed;&lt;br /&gt;
* sesskey values are checked before all write actions where appropriate (some read actions as well);&lt;br /&gt;
* capabilities are checked where roles differ; and&lt;br /&gt;
* if the issue itself is a [[Security|security]] issue, the [[Process#Security_issues|security process]] is being followed.&lt;br /&gt;
** Ensure that the fix is &#039;&#039;&#039;not&#039;&#039;&#039; available in a public repository (ie. a personal Github account); stand-alone patches should be provided instead.&lt;br /&gt;
** The issue will not be integrated until just before the next minor version release.&lt;br /&gt;
&lt;br /&gt;
==Documentation==&lt;br /&gt;
Work does not stop when code is integrated.&lt;br /&gt;
&lt;br /&gt;
Ensure that:&lt;br /&gt;
* Appropriate [[Tracker_issue_labels|labels]] have been added when there has been a function change, particularly&lt;br /&gt;
** qa_test_required (significant functional change),&lt;br /&gt;
** docs_required (any functional change, usually paired with ui_change),&lt;br /&gt;
** dev_docs_required (any change to APIs, usually paired with api_chage),&lt;br /&gt;
** ui_change (any functional change, usually paired with docs_required, except ui_change remains permanetly), and&lt;br /&gt;
** api_change (any change to APIs that devs will need to know about, usually paired with dev_docs_required, except api_change remains permanetly).&lt;br /&gt;
&lt;br /&gt;
==Git==&lt;br /&gt;
Ensure that:&lt;br /&gt;
* the commit message includes the tracker issue number and ideally the component (ideal format is &amp;lt;code&amp;gt;MDL-xxxx Component: Commit message&amp;lt;/code&amp;gt;);&lt;br /&gt;
* the commit message makes sense and describes what is going on in the patch. (see [[Commit_cheat_sheet]] for some guidance)&lt;br /&gt;
* the Git history is clean and the work has been rebased to logical commits; and&lt;br /&gt;
* the original author of the work provided as a patch has been given credit within the commit (as author of in the commit message if changes were made).&lt;br /&gt;
&lt;br /&gt;
==Sanity check==&lt;br /&gt;
Ensure that:&lt;br /&gt;
* the code seems to solve the described problem completely within its reported scope (and further issues have been created to resolve remaining parts or further refactoring);&lt;br /&gt;
* the code makes sense in relation to the broader codebase (look at the whole function, not just the altered code); and&lt;br /&gt;
* the developer has searched for and fixed other areas that may also have been affected by the same problem.&lt;br /&gt;
&lt;br /&gt;
==See Also==&lt;br /&gt;
* [http://moodle.org/plugins/view.php?plugin=local_codechecker Code checker plugin]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42127</id>
		<title>HTML Guidelines</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=HTML_Guidelines&amp;diff=42127"/>
		<updated>2013-08-19T06:08:35Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: Created page with &amp;quot;==Header== Header usage should be hierarchical within page views Reference link: Standardize classnames and layout to facilitate theming  * Headers should be hierarchical,...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Header==&lt;br /&gt;
Header usage should be hierarchical within page views&lt;br /&gt;
Reference link: [[Standardize classnames and layout to facilitate theming]]&lt;br /&gt;
&lt;br /&gt;
* Headers should be hierarchical, with two or more &amp;amp;lt;h3&amp;amp;gt; under each &amp;amp;lt;h2&amp;amp;gt; and so forth. &lt;br /&gt;
* &amp;amp;lt;h2&amp;amp;gt; is the top-level header of the page content/central column. There should be only one &amp;amp;lt;h2&amp;amp;gt;&lt;br /&gt;
* Headers should not be used for notification. Notification classes should be used for notification.&lt;br /&gt;
* We shouldn&#039;t even need the .main class. The fact that the header is located in the central column/main content area, and is a header, should be enough. The only reason we need it is because we&#039;re recruiting headers for other purposes. &lt;br /&gt;
&lt;br /&gt;
Not this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;These is your score on XX activity.&amp;lt;/h2&amp;gt;&lt;br /&gt;
   &amp;lt;p&amp;gt;A paragraph of content.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h2 class=&amp;quot;main&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Generic submit button&amp;quot;&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This: &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  &amp;lt;h2&amp;gt;This is the top-level header&amp;lt;/h2&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;This paragraph gives additional information and explains what&#039;s to come.&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;h3&amp;gt;Content sub-header&amp;lt;/h3&amp;gt;&lt;br /&gt;
  &amp;lt;p&amp;gt;More paragraph content content content&amp;lt;/p&amp;gt;&lt;br /&gt;
  &amp;lt;div class=&amp;quot;notifyproblem&amp;quot;&amp;gt;You must make a selection to proceed.&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Timeformat&amp;diff=35606</id>
		<title>Timeformat</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Timeformat&amp;diff=35606"/>
		<updated>2012-09-21T09:05:42Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: /* Timeformat settings */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Timeformat&lt;br /&gt;
|state = In Development&lt;br /&gt;
|tracker = http://tracker.moodle.org/browse/MDL-11094&lt;br /&gt;
|discussion = &lt;br /&gt;
|assignee = Rossiani Wijaya&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Timeformat settings==&lt;br /&gt;
Administrator can change the following location settings in &#039;&#039;Settings &amp;gt; Site administration &amp;gt; Location &amp;gt; Location settings &amp;gt; Time display format&#039;&#039;.&lt;br /&gt;
User can change the timeformat setting in &#039;&#039;my profile settings &amp;gt; edit profile &amp;gt; timeformat&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==Timeformat options==&lt;br /&gt;
There are three options available for displaying timeformat:&lt;br /&gt;
# Default&lt;br /&gt;
# 12 hours&lt;br /&gt;
# 24 hours&lt;br /&gt;
&lt;br /&gt;
====Default format====&lt;br /&gt;
The default value for timeformat is specify according to the language that used to display the content for the site.&lt;br /&gt;
&lt;br /&gt;
Both site and user setting is automatically assign to &#039;&#039;Default&#039;&#039; value.&lt;br /&gt;
&lt;br /&gt;
====12 hours format====&lt;br /&gt;
Hour is display between 0-12.  In addition, this format will also display the AM/PM time definition.  An example for displaying the time for 3 O&#039;clock in the afternoon is 3:00 pm.&lt;br /&gt;
&lt;br /&gt;
====24 hours format====&lt;br /&gt;
Hour is display between 0-24.  An example for displaying the time for 3 O&#039;clock in the afternoon is 15:00.&lt;br /&gt;
&lt;br /&gt;
==Hierarchy for timeformat ==&lt;br /&gt;
If user specify their timeformat setting, it would overwrite the site setting.&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Timeformat&amp;diff=35605</id>
		<title>Timeformat</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Timeformat&amp;diff=35605"/>
		<updated>2012-09-21T08:50:14Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Timeformat&lt;br /&gt;
|state = In Development&lt;br /&gt;
|tracker = http://tracker.moodle.org/browse/MDL-11094&lt;br /&gt;
|discussion = &lt;br /&gt;
|assignee = Rossiani Wijaya&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
==Timeformat settings==&lt;br /&gt;
Administrator can change the following location settings in &#039;&#039;Settings &amp;gt; Site administration &amp;gt; Advanced features &amp;gt; Time display format&#039;&#039;.&lt;br /&gt;
User can change the timeformat setting in &#039;&#039;my profile settings &amp;gt; edit profile &amp;gt; timeformat&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==Timeformat options==&lt;br /&gt;
There are three options available for displaying timeformat:&lt;br /&gt;
# Default&lt;br /&gt;
# 12 hours&lt;br /&gt;
# 24 hours&lt;br /&gt;
&lt;br /&gt;
====Default format====&lt;br /&gt;
The default value for timeformat is specify according to the language that used to display the content for the site.&lt;br /&gt;
&lt;br /&gt;
Both site and user setting is automatically assign to &#039;&#039;Default&#039;&#039; value.&lt;br /&gt;
&lt;br /&gt;
====12 hours format====&lt;br /&gt;
Hour is display between 0-12.  In addition, this format will also display the AM/PM time definition.  An example for displaying the time for 3 O&#039;clock in the afternoon is 3:00 pm.&lt;br /&gt;
&lt;br /&gt;
====24 hours format====&lt;br /&gt;
Hour is display between 0-24.  An example for displaying the time for 3 O&#039;clock in the afternoon is 15:00.&lt;br /&gt;
&lt;br /&gt;
==Hierarchy for timeformat ==&lt;br /&gt;
If user specify their timeformat setting, it would overwrite the site setting.&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Timeformat&amp;diff=35595</id>
		<title>Timeformat</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Timeformat&amp;diff=35595"/>
		<updated>2012-09-18T06:33:47Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Timeformat&lt;br /&gt;
|state = In Development&lt;br /&gt;
|tracker = http://tracker.moodle.org/browse/MDL-11094&lt;br /&gt;
|discussion = &lt;br /&gt;
|assignee = Rossiani Wijaya&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
{{Managing a Moodle site}}&lt;br /&gt;
==Timeformat settings==&lt;br /&gt;
Administrator can change the following location settings in &#039;&#039;Settings &amp;gt; Site administration &amp;gt; Advanced features &amp;gt; Time display format&#039;&#039;.&lt;br /&gt;
User can change the timeformat setting in &#039;&#039;my profile settings &amp;gt; edit profile &amp;gt; timeformat&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
==Timeformat options==&lt;br /&gt;
There are three options available for displaying timeformat:&lt;br /&gt;
# Default&lt;br /&gt;
# 12 hours&lt;br /&gt;
# 24 hours&lt;br /&gt;
&lt;br /&gt;
====Default format====&lt;br /&gt;
The default value for timeformat is specify according to the language that used to display the content for the site.&lt;br /&gt;
&lt;br /&gt;
Both site and user setting is automatically assign to &#039;&#039;Default&#039;&#039; value.&lt;br /&gt;
&lt;br /&gt;
====12 hours format====&lt;br /&gt;
Hour is display between 0-12.  An example for displaying the time for 3 O&#039;clock in the afternoon is 3:00 pm.&lt;br /&gt;
&lt;br /&gt;
====24 hours format====&lt;br /&gt;
Hour is display between 0-24.  An example for displaying the time for 3 O&#039;clock in the afternoon is 15:00.&lt;br /&gt;
&lt;br /&gt;
==Hierarchy for timeformat ==&lt;br /&gt;
If user specify their timeformat setting, it would overwrite the site setting.&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Timeformat&amp;diff=35594</id>
		<title>Timeformat</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Timeformat&amp;diff=35594"/>
		<updated>2012-09-18T06:13:40Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Timeformat&lt;br /&gt;
|state = In Development&lt;br /&gt;
|tracker = http://tracker.moodle.org/browse/MDL-11094&lt;br /&gt;
|discussion = &lt;br /&gt;
|assignee = Rossiani Wijaya&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Managing a Moodle site}}&lt;br /&gt;
==Timeformat settings==&lt;br /&gt;
Administrator can change the following location settings in &#039;&#039;Settings &amp;gt; Site administration &amp;gt; Advanced features &amp;gt; Time display format&#039;&#039;.&lt;br /&gt;
User can change the timeformat setting in &#039;&#039;my profile settings &amp;gt; edit profile &amp;gt; timeformat&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
====Default timeformat====&lt;br /&gt;
The default value for timeformat is specify according to the language that used to display the content for the site.&lt;br /&gt;
&lt;br /&gt;
Both site and user setting is automatically assign to &#039;&#039;Default&#039;&#039; value.&lt;br /&gt;
&lt;br /&gt;
====Hierarchy for timeformat ====&lt;br /&gt;
If user specify their timeformat setting, it would overwrite the site setting.&lt;br /&gt;
&lt;br /&gt;
====Options value for timeformat ====&lt;br /&gt;
There are two options available for displaying timeformat:&lt;br /&gt;
1. 12 hours&lt;br /&gt;
2. 24 hours&lt;br /&gt;
&lt;br /&gt;
====Examples for timeformat ====&lt;br /&gt;
An example for displaying the time for 3 O&#039;clock in the afternoon:&lt;br /&gt;
- 12 hours format: 3:00 pm&lt;br /&gt;
- 24 hours format: 15:00&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Timeformat&amp;diff=35593</id>
		<title>Timeformat</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Timeformat&amp;diff=35593"/>
		<updated>2012-09-18T05:14:31Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: Created page with &amp;quot;{{Infobox Project |name = Timeformat |state = In Development |tracker = http://tracker.moodle.org/browse/MDL-11094 |discussion =  |assignee = Rossiani Wijaya }}   {{Managing a Mo...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Infobox Project&lt;br /&gt;
|name = Timeformat&lt;br /&gt;
|state = In Development&lt;br /&gt;
|tracker = http://tracker.moodle.org/browse/MDL-11094&lt;br /&gt;
|discussion = &lt;br /&gt;
|assignee = Rossiani Wijaya&lt;br /&gt;
}}&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
{{Managing a Moodle site}}&lt;br /&gt;
==Timeformat settings==&lt;br /&gt;
Administrator can change the following location settings in &#039;&#039;Settings &amp;gt; Site administration &amp;gt; Advanced features &amp;gt; Time display format&#039;&#039;.&lt;br /&gt;
User can change the timeformat setting in &#039;&#039;my profile settings &amp;gt; edit profile &amp;gt; timeformat&#039;&#039;.&lt;br /&gt;
&lt;br /&gt;
====Default timeformat====&lt;br /&gt;
The default value for timeformat is specify according to the language that used to display the content for the site.&lt;br /&gt;
&lt;br /&gt;
Both site and user setting is automatically assign to &#039;&#039;Default&#039;&#039; value.&lt;br /&gt;
&lt;br /&gt;
====Hierarchy for timeformat ====&lt;br /&gt;
If user specify their timeformat setting, it would overwrite the site setting.&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31885</id>
		<title>Portfolio plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31885"/>
		<updated>2012-01-27T09:39:33Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: /* mnet_publishes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).&lt;br /&gt;
&lt;br /&gt;
===Error handling===&lt;br /&gt;
Your code should always throw exceptions of type portfolio_plugin_exception.  You should NOT call error or print_error or whatever, and also any third party exceptions should really be caught and rethrown although most of the time other exceptions will be caught too by the portfolio code (although this will trigger a warning in debug mode)&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
If your plugin isn&#039;t going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/. &lt;br /&gt;
&lt;br /&gt;
===File access===&lt;br /&gt;
&#039;&#039;&#039;All&#039;&#039;&#039; of the handling of files using the Files API should go through the portfolio exporter object.  There are various helper methods you can use (see prepare_package documentation) in the exporter object.  The reason for this is that this object is mocked during unit tests so that files don&#039;t actually get moved around while tests are running.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
==Template==&lt;br /&gt;
Please refer to portfolio goodledocs plugins ([http://git.moodle.org/gw?p=moodle.git;a=tree;f=portfolio/googledocs;h=6d5e67e99545dc7430df5ad065d4d5da8945417d;hb=master portfolio/googledocs/]) as template reference to start with.&lt;br /&gt;
&lt;br /&gt;
==Naming convention==&lt;br /&gt;
&lt;br /&gt;
Plugin class name should start with portfolio_plugin_&#039;&#039;&#039;&#039;&#039;PLUGINNAME&#039;&#039;&#039;&#039;&#039;. This class should also extends from &#039;&#039;&#039;portfolio_plugin_push_base&#039;&#039;&#039; or &#039;&#039;&#039;portfolio_plugin_pull_base&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class portfolio_plugin_foo extends portfolio_plugin_push_base {}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
For more information regarding naming convention, please refer to: [[Frankenstyle|Frankenstyle page]]&lt;br /&gt;
&lt;br /&gt;
==File structure==&lt;br /&gt;
For the purposes of this tutorial, it will be assumed you want to create a plugin called &amp;quot;&#039;&#039;foo&#039;&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Create a new directory in portfolio/ for the new plugin.  The name for this directory should refer to plugin&#039;s name, for example: portfolio&#039;&#039;/foo&#039;&#039;.&lt;br /&gt;
# Create the standard [[version.php]] in here that you would normally create for most other plugin types in Moodle.&lt;br /&gt;
# Create a &#039;&#039;lib.php&#039;&#039; inside the plugin directory, for example: portfolio/foo&#039;&#039;&#039;/lib.php&#039;&#039;&#039;.&lt;br /&gt;
# Create the necessary subclass to make our plugin work.  The subclass must be called portfolio_plugin&#039;&#039;&#039;&#039;&#039;_PLUGINNAME&#039;&#039;&#039;&#039;&#039; (eg: portfolio_plugin_foo) and directly extend either &#039;&#039;portfolio_plugin_push_base&#039;&#039; or &#039;&#039;portfolio_plugin_pull_base&#039;&#039;.   The only real differences between them are that one pushes the package directly to the remote system (usually via a HTTP POST, but could be filesystem based), and the other type (pull) requires the remote system to request it.&lt;br /&gt;
# Create file for plugin language strings:&lt;br /&gt;
## create directory &#039;&#039;&#039;lang/&#039;&#039;&#039; and subdirectory &#039;&#039;&#039;$language/&#039;&#039;&#039; within the plugin, for example: portfolio/foo&#039;&#039;&#039;/lang/$language/&#039;&#039;&#039;.  English (en) language must exist as this language will be use as default language string. &lt;br /&gt;
## create php file using this naming convention, portfolio_&#039;&#039;&#039;PLUGINNAME&#039;&#039;&#039;.php, for example: /portfolio/foo/lang/en/&#039;&#039;&#039;portfolio_foo.php&#039;&#039;&#039;&lt;br /&gt;
# Optionally, this plugin could store some information in database.  In order to store data to database, create &#039;&#039;&#039;db/&#039;&#039;&#039; directory within the plugin directory, for example: portfolio/foo/&#039;&#039;&#039;db/&#039;&#039;&#039;.  Also, create &#039;&#039;&#039;install.xml&#039;&#039;&#039; for the table schema.  For further details using database table, please refer to: [[XMLDB editor]] and  [[Database_FAQ#XMLDB|Database FAQ &amp;gt; XMLDB]].&lt;br /&gt;
Read the phpdoc documentation for the base classes for further information about each function, including the arguments it takes and the expected return type.  The following documentation serves as an overview.&lt;br /&gt;
&lt;br /&gt;
==Interfacing to APIs==&lt;br /&gt;
&lt;br /&gt;
===Methods you must override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====get_name=====&lt;br /&gt;
&lt;br /&gt;
Return a localized name for this plugin. Usually just something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;pluginname&#039;, &#039;portfolio_yourplugin&#039;); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is enforced as an abstract function rather than a loose fuzzy dependence on a language string for maximum ease when developing a new plugin (it&#039;s blindly obvious you must implement it ;) )&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====prepare_package=====&lt;br /&gt;
&lt;br /&gt;
Does anything necessary to prepare the package for sending.  This might be writing out a metadata manifest file, or zipping up all the files in the temporary directory.  This is called after the corresponding prepare_package method in the caller, which writes files out to a temporary location.  Files can then be retrieved from there using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles, which returns an array of stored_file objects.  You can zip files using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;zip_tempfiles.  You &#039;&#039;&#039;must&#039;&#039;&#039; not use the files api directly.&lt;br /&gt;
&lt;br /&gt;
=====send_package=====&lt;br /&gt;
&lt;br /&gt;
Actually send the package to the remote system (this might be just sending an xmlrpc request to say a file is ready for the remote system to fetch, (in which case you must set the $file member variable for portfolio/file.php later, or actually transfering the file). You can retrieve the files the caller has written out using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles() which returns an array of stored_file objects.&lt;br /&gt;
&lt;br /&gt;
=====get_interactive_continue_url=====&lt;br /&gt;
&lt;br /&gt;
Return a url to present to the user as a &#039;continue to their portfolio&#039; link.  This can return false, although then you might want to implement get_extra_finish_options.   See also get_static_continue_url and resolve_static_continue_url&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====verify_file_request_params (pull only)=====&lt;br /&gt;
&lt;br /&gt;
When remote systems request files, access control check is delegated to the plugin, by passing the array of request parameters. Should this function return false, access to the file will be denied.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====expected_time=====&lt;br /&gt;
&lt;br /&gt;
How long a transfer can reasonably expect to take.  This function is passed the estimate from the caller (which usually takes into account filesize) and can either agree with it by returning whatever it is given, or override it.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH. 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;
&lt;br /&gt;
===Methods you can override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====plugin_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
If the plugin depends on some other part of Moodle being configured in a particular way (for example if your plugin relies on using mnet for transport and mnet is off), you can override this function.  It is called frequently in different places in the code, and whenever a non empty value is returned (an error code), all instances of your plugin will be set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====has_admin_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has some admininistrative configuration settings (rather than by the user), override this plugin to return true. You must also override admin_config_form and get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_config=====&lt;br /&gt;
&lt;br /&gt;
Because most of the handling of the getting and setting of admin entered configuration data happens in the parent class, with no need to be overridden in the subclasses, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====allows_multiple_instances=====&lt;br /&gt;
&lt;br /&gt;
By default, all plugins can have multiple instances configured.  If it&#039;s only sensible for your plugin to be able to have one instance configured, override this to return false.&lt;br /&gt;
&lt;br /&gt;
=====allows_multiple_exports=====&lt;br /&gt;
By default, all plugin support multiple export configured.  However, if it require a redirect for authentication and doesn&#039;t support dynamic constructed urls to return to, overide this to return false.&lt;br /&gt;
&lt;br /&gt;
=====file_mime_check=====&lt;br /&gt;
This function need to be override to return false, if the file type have restriction on mimetypes.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
=====supported_formats=====&lt;br /&gt;
The formats this plugin 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 parent class defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
=====has_user_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can be configured in the user profile section, override this function to return true. You must also override user_config_form and get_allowed_user_config.  You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====has_export_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can have further (user) configuration during export, override this function to return true. You must also override export_config_form, get_export_summary and get_allowed_export_config.  You can additionally override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_form=====&lt;br /&gt;
&lt;br /&gt;
This function can actually be called statically or non statically, depending on whether it&#039;s for editing an existing instance of the plugin, or for creating a new one.  It&#039;s passed an mform object by reference, as are the other two config form functions, to add elements to it.  If you override this you don&#039;t need to handle setting the data of the elements (when editing), that&#039;s done in the caller, using get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====user_config_form=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_user_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.   You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====export_config_form=====&lt;br /&gt;
    &lt;br /&gt;
If your plugin has overridden has_export_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.  You can also override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_validation=====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object. This function can be called statically or non statically.&lt;br /&gt;
&lt;br /&gt;
=====user_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;
=====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_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 an array of config options, the keys being nice strings to display to the user and the values being the selected config values.&lt;br /&gt;
&lt;br /&gt;
=====steal_control=====&lt;br /&gt;
&lt;br /&gt;
During any part of the export process, a plugin can completely steal control away from portfolio/add.php. This is useful, for example, for plugins that need the user go to log into a remote system and grant an application access.  It could be also used for a completely custom screen provided by the plugin.  If you need this, override this function to return a url, and the user will be redirected there.  When you&#039;re finished, return to $CFG-&amp;gt;wwwroot/portfolio/add.php?postcontrol=1 and processing will continue.   If you override this, it might be useful to also override post_control.&lt;br /&gt;
&lt;br /&gt;
=====post_control=====&lt;br /&gt;
&lt;br /&gt;
After control is returned after steal_control, post_control will be called before the next stage is processed, and passed any request parameters.  For an example of how this is used, see the box.net plugin, which uses steal_control to redirect to box.net to get the user to authenticate and then box.net redirects back to a url passing an authentication token to use for the rest of the session.  Since it&#039;s part of the request parameters, it&#039;s passed through to post_control, which stores whatever it needs before the next stage.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_user_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of user entered configuration data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_export_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of per-export config data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed (note that if you store non-user entered config data you must implement this function)&lt;br /&gt;
&lt;br /&gt;
=====instance_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
Similar to plugin_sanity_check although operates on an instance basis.   Again, returning anything non empty here will be taken as an error string, and the instance set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====send_file (pull only)=====&lt;br /&gt;
&lt;br /&gt;
Sends the file to STDOUT (the browser in the case of the download plugin but may be an external system requesting it) - this function gets called when portfolio/file.php is requested and by default just passes the contents of the file straight through to the browser, unencrypted.  The other pull plugin this far is mahara, and it doesn&#039;t implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.&lt;br /&gt;
&lt;br /&gt;
=====cleanup=====&lt;br /&gt;
&lt;br /&gt;
if you&#039;ve stashed anything in extra database tables, you can implement this function to clean them up.  this is called on cron to cleanup expired transfers, as well as after a successful transfer.&lt;br /&gt;
&lt;br /&gt;
=====mnet_publishes (&#039;&#039;deprecated on Moodle 2.0&#039;&#039;)=====&lt;br /&gt;
&lt;br /&gt;
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)&lt;br /&gt;
&lt;br /&gt;
=====get_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
In some cases, the transfer is logged from somewhere other than the interactive browser session (eg when a pull plugin completes the transfer before the browser is redirected to the finish page). The static_continue_url is the one that is finally logged and inserted into the log table, and used to display later to the user (in the Transfer log screen under portfolios in their profile).  This, combined with resolve_static_continue_url, are used to generate a new url later to the user.&lt;br /&gt;
&lt;br /&gt;
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid.&lt;br /&gt;
The static continue url just contains the &amp;quot;wantsurl&amp;quot; part of that in mahara - eg artefact/file/&lt;br /&gt;
resolve_static_continue_url converts the static url into a new mnet jump session url.&lt;br /&gt;
&lt;br /&gt;
=====resolve_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
See get_static_continue_url.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Methods you shouldn&#039;t really override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====create_instance=====&lt;br /&gt;
&lt;br /&gt;
Creates an instance of the given plugin.  As well as plugin and name, this can also be passed initial admin config. Returns an object of the correct subclass.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====__construct=====&lt;br /&gt;
&lt;br /&gt;
Constructor for the plugin.  Subclasses don&#039;t need to override this.&lt;br /&gt;
&lt;br /&gt;
=====save=====&lt;br /&gt;
&lt;br /&gt;
Saves data stored in the object back to the database and resets the dirty flag.&lt;br /&gt;
&lt;br /&gt;
=====delete=====&lt;br /&gt;
&lt;br /&gt;
Deletes this instance and all configuration data associated with it completely from the database&lt;br /&gt;
&lt;br /&gt;
=====set_export_config=====&lt;br /&gt;
&lt;br /&gt;
I had originally made this function final in the base class, but the box.net plugin needs to override it for dependent export config that&#039;s a bit funky.&lt;br /&gt;
&lt;br /&gt;
==Database tables==&lt;br /&gt;
In case we need to have a database table that holds some specific information used for the portfolio plugin, we will need to create the file &#039;&#039;/portfolio/foo/lib/&#039;&#039;&#039;install.xml&#039;&#039;&#039;&#039;&#039; with the table schema contained within it.&lt;br /&gt;
&lt;br /&gt;
To create the install.xml file, use the [[XMLDB editor]]. See [[Database_FAQ#XMLDB|Database FAQ &amp;gt; XMLDB]] for further details.&lt;br /&gt;
&lt;br /&gt;
Up-to-date documentation on upgrading portfolio plugin, as well as providing new capabilities and events to the system, can be found under [https://docs.moodle.org/dev/Installing_and_upgrading_plugin_database_tables#install.php Installing and Upgrading Plugin Database Tables]&lt;br /&gt;
&lt;br /&gt;
[[Category:Portfolios]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31884</id>
		<title>Portfolio plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31884"/>
		<updated>2012-01-27T09:17:22Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).&lt;br /&gt;
&lt;br /&gt;
===Error handling===&lt;br /&gt;
Your code should always throw exceptions of type portfolio_plugin_exception.  You should NOT call error or print_error or whatever, and also any third party exceptions should really be caught and rethrown although most of the time other exceptions will be caught too by the portfolio code (although this will trigger a warning in debug mode)&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
If your plugin isn&#039;t going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/. &lt;br /&gt;
&lt;br /&gt;
===File access===&lt;br /&gt;
&#039;&#039;&#039;All&#039;&#039;&#039; of the handling of files using the Files API should go through the portfolio exporter object.  There are various helper methods you can use (see prepare_package documentation) in the exporter object.  The reason for this is that this object is mocked during unit tests so that files don&#039;t actually get moved around while tests are running.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
==Template==&lt;br /&gt;
Please refer to portfolio goodledocs plugins ([http://git.moodle.org/gw?p=moodle.git;a=tree;f=portfolio/googledocs;h=6d5e67e99545dc7430df5ad065d4d5da8945417d;hb=master portfolio/googledocs/]) as template reference to start with.&lt;br /&gt;
&lt;br /&gt;
==Naming convention==&lt;br /&gt;
&lt;br /&gt;
Plugin class name should start with portfolio_plugin_&#039;&#039;&#039;&#039;&#039;PLUGINNAME&#039;&#039;&#039;&#039;&#039;. This class should also extends from &#039;&#039;&#039;portfolio_plugin_push_base&#039;&#039;&#039; or &#039;&#039;&#039;portfolio_plugin_pull_base&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class portfolio_plugin_foo extends portfolio_plugin_push_base {}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
For more information regarding naming convention, please refer to: [[Frankenstyle|Frankenstyle page]]&lt;br /&gt;
&lt;br /&gt;
==File structure==&lt;br /&gt;
For the purposes of this tutorial, it will be assumed you want to create a plugin called &amp;quot;&#039;&#039;foo&#039;&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Create a new directory in portfolio/ for the new plugin.  The name for this directory should refer to plugin&#039;s name, for example: portfolio&#039;&#039;/foo&#039;&#039;.&lt;br /&gt;
# Create the standard [[version.php]] in here that you would normally create for most other plugin types in Moodle.&lt;br /&gt;
# Create a &#039;&#039;lib.php&#039;&#039; inside the plugin directory, for example: portfolio/foo&#039;&#039;&#039;/lib.php&#039;&#039;&#039;.&lt;br /&gt;
# Create the necessary subclass to make our plugin work.  The subclass must be called portfolio_plugin&#039;&#039;&#039;&#039;&#039;_PLUGINNAME&#039;&#039;&#039;&#039;&#039; (eg: portfolio_plugin_foo) and directly extend either &#039;&#039;portfolio_plugin_push_base&#039;&#039; or &#039;&#039;portfolio_plugin_pull_base&#039;&#039;.   The only real differences between them are that one pushes the package directly to the remote system (usually via a HTTP POST, but could be filesystem based), and the other type (pull) requires the remote system to request it.&lt;br /&gt;
# Create file for plugin language strings:&lt;br /&gt;
## create directory &#039;&#039;&#039;lang/&#039;&#039;&#039; and subdirectory &#039;&#039;&#039;$language/&#039;&#039;&#039; within the plugin, for example: portfolio/foo&#039;&#039;&#039;/lang/$language/&#039;&#039;&#039;.  English (en) language must exist as this language will be use as default language string. &lt;br /&gt;
## create php file using this naming convention, portfolio_&#039;&#039;&#039;PLUGINNAME&#039;&#039;&#039;.php, for example: /portfolio/foo/lang/en/&#039;&#039;&#039;portfolio_foo.php&#039;&#039;&#039;&lt;br /&gt;
# Optionally, this plugin could store some information in database.  In order to store data to database, create &#039;&#039;&#039;db/&#039;&#039;&#039; directory within the plugin directory, for example: portfolio/foo/&#039;&#039;&#039;db/&#039;&#039;&#039;.  Also, create &#039;&#039;&#039;install.xml&#039;&#039;&#039; for the table schema.  For further details using database table, please refer to: [[XMLDB editor]] and  [[Database_FAQ#XMLDB|Database FAQ &amp;gt; XMLDB]].&lt;br /&gt;
Read the phpdoc documentation for the base classes for further information about each function, including the arguments it takes and the expected return type.  The following documentation serves as an overview.&lt;br /&gt;
&lt;br /&gt;
==Interfacing to APIs==&lt;br /&gt;
&lt;br /&gt;
===Methods you must override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====get_name=====&lt;br /&gt;
&lt;br /&gt;
Return a localized name for this plugin. Usually just something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;pluginname&#039;, &#039;portfolio_yourplugin&#039;); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is enforced as an abstract function rather than a loose fuzzy dependence on a language string for maximum ease when developing a new plugin (it&#039;s blindly obvious you must implement it ;) )&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====prepare_package=====&lt;br /&gt;
&lt;br /&gt;
Does anything necessary to prepare the package for sending.  This might be writing out a metadata manifest file, or zipping up all the files in the temporary directory.  This is called after the corresponding prepare_package method in the caller, which writes files out to a temporary location.  Files can then be retrieved from there using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles, which returns an array of stored_file objects.  You can zip files using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;zip_tempfiles.  You &#039;&#039;&#039;must&#039;&#039;&#039; not use the files api directly.&lt;br /&gt;
&lt;br /&gt;
=====send_package=====&lt;br /&gt;
&lt;br /&gt;
Actually send the package to the remote system (this might be just sending an xmlrpc request to say a file is ready for the remote system to fetch, (in which case you must set the $file member variable for portfolio/file.php later, or actually transfering the file). You can retrieve the files the caller has written out using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles() which returns an array of stored_file objects.&lt;br /&gt;
&lt;br /&gt;
=====get_interactive_continue_url=====&lt;br /&gt;
&lt;br /&gt;
Return a url to present to the user as a &#039;continue to their portfolio&#039; link.  This can return false, although then you might want to implement get_extra_finish_options.   See also get_static_continue_url and resolve_static_continue_url&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====verify_file_request_params (pull only)=====&lt;br /&gt;
&lt;br /&gt;
When remote systems request files, access control check is delegated to the plugin, by passing the array of request parameters. Should this function return false, access to the file will be denied.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====expected_time=====&lt;br /&gt;
&lt;br /&gt;
How long a transfer can reasonably expect to take.  This function is passed the estimate from the caller (which usually takes into account filesize) and can either agree with it by returning whatever it is given, or override it.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH. 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;
&lt;br /&gt;
===Methods you can override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====plugin_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
If the plugin depends on some other part of Moodle being configured in a particular way (for example if your plugin relies on using mnet for transport and mnet is off), you can override this function.  It is called frequently in different places in the code, and whenever a non empty value is returned (an error code), all instances of your plugin will be set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====has_admin_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has some admininistrative configuration settings (rather than by the user), override this plugin to return true. You must also override admin_config_form and get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_config=====&lt;br /&gt;
&lt;br /&gt;
Because most of the handling of the getting and setting of admin entered configuration data happens in the parent class, with no need to be overridden in the subclasses, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====allows_multiple_instances=====&lt;br /&gt;
&lt;br /&gt;
By default, all plugins can have multiple instances configured.  If it&#039;s only sensible for your plugin to be able to have one instance configured, override this to return false.&lt;br /&gt;
&lt;br /&gt;
=====allows_multiple_exports=====&lt;br /&gt;
By default, all plugin support multiple export configured.  However, if it require a redirect for authentication and doesn&#039;t support dynamic constructed urls to return to, overide this to return false.&lt;br /&gt;
&lt;br /&gt;
=====file_mime_check=====&lt;br /&gt;
This function need to be override to return false, if the file type have restriction on mimetypes.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
=====supported_formats=====&lt;br /&gt;
The formats this plugin 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 parent class defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
=====has_user_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can be configured in the user profile section, override this function to return true. You must also override user_config_form and get_allowed_user_config.  You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====has_export_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can have further (user) configuration during export, override this function to return true. You must also override export_config_form, get_export_summary and get_allowed_export_config.  You can additionally override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_form=====&lt;br /&gt;
&lt;br /&gt;
This function can actually be called statically or non statically, depending on whether it&#039;s for editing an existing instance of the plugin, or for creating a new one.  It&#039;s passed an mform object by reference, as are the other two config form functions, to add elements to it.  If you override this you don&#039;t need to handle setting the data of the elements (when editing), that&#039;s done in the caller, using get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====user_config_form=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_user_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.   You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====export_config_form=====&lt;br /&gt;
    &lt;br /&gt;
If your plugin has overridden has_export_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.  You can also override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_validation=====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object. This function can be called statically or non statically.&lt;br /&gt;
&lt;br /&gt;
=====user_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;
=====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_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 an array of config options, the keys being nice strings to display to the user and the values being the selected config values.&lt;br /&gt;
&lt;br /&gt;
=====steal_control=====&lt;br /&gt;
&lt;br /&gt;
During any part of the export process, a plugin can completely steal control away from portfolio/add.php. This is useful, for example, for plugins that need the user go to log into a remote system and grant an application access.  It could be also used for a completely custom screen provided by the plugin.  If you need this, override this function to return a url, and the user will be redirected there.  When you&#039;re finished, return to $CFG-&amp;gt;wwwroot/portfolio/add.php?postcontrol=1 and processing will continue.   If you override this, it might be useful to also override post_control.&lt;br /&gt;
&lt;br /&gt;
=====post_control=====&lt;br /&gt;
&lt;br /&gt;
After control is returned after steal_control, post_control will be called before the next stage is processed, and passed any request parameters.  For an example of how this is used, see the box.net plugin, which uses steal_control to redirect to box.net to get the user to authenticate and then box.net redirects back to a url passing an authentication token to use for the rest of the session.  Since it&#039;s part of the request parameters, it&#039;s passed through to post_control, which stores whatever it needs before the next stage.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_user_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of user entered configuration data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_export_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of per-export config data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed (note that if you store non-user entered config data you must implement this function)&lt;br /&gt;
&lt;br /&gt;
=====instance_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
Similar to plugin_sanity_check although operates on an instance basis.   Again, returning anything non empty here will be taken as an error string, and the instance set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====send_file (pull only)=====&lt;br /&gt;
&lt;br /&gt;
Sends the file to STDOUT (the browser in the case of the download plugin but may be an external system requesting it) - this function gets called when portfolio/file.php is requested and by default just passes the contents of the file straight through to the browser, unencrypted.  The other pull plugin this far is mahara, and it doesn&#039;t implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.&lt;br /&gt;
&lt;br /&gt;
=====cleanup=====&lt;br /&gt;
&lt;br /&gt;
if you&#039;ve stashed anything in extra database tables, you can implement this function to clean them up.  this is called on cron to cleanup expired transfers, as well as after a successful transfer.&lt;br /&gt;
&lt;br /&gt;
=====mnet_publishes=====&lt;br /&gt;
&lt;br /&gt;
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)&lt;br /&gt;
&lt;br /&gt;
=====get_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
In some cases, the transfer is logged from somewhere other than the interactive browser session (eg when a pull plugin completes the transfer before the browser is redirected to the finish page). The static_continue_url is the one that is finally logged and inserted into the log table, and used to display later to the user (in the Transfer log screen under portfolios in their profile).  This, combined with resolve_static_continue_url, are used to generate a new url later to the user.&lt;br /&gt;
&lt;br /&gt;
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid.&lt;br /&gt;
The static continue url just contains the &amp;quot;wantsurl&amp;quot; part of that in mahara - eg artefact/file/&lt;br /&gt;
resolve_static_continue_url converts the static url into a new mnet jump session url.&lt;br /&gt;
&lt;br /&gt;
=====resolve_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
See get_static_continue_url.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Methods you shouldn&#039;t really override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====create_instance=====&lt;br /&gt;
&lt;br /&gt;
Creates an instance of the given plugin.  As well as plugin and name, this can also be passed initial admin config. Returns an object of the correct subclass.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====__construct=====&lt;br /&gt;
&lt;br /&gt;
Constructor for the plugin.  Subclasses don&#039;t need to override this.&lt;br /&gt;
&lt;br /&gt;
=====save=====&lt;br /&gt;
&lt;br /&gt;
Saves data stored in the object back to the database and resets the dirty flag.&lt;br /&gt;
&lt;br /&gt;
=====delete=====&lt;br /&gt;
&lt;br /&gt;
Deletes this instance and all configuration data associated with it completely from the database&lt;br /&gt;
&lt;br /&gt;
=====set_export_config=====&lt;br /&gt;
&lt;br /&gt;
I had originally made this function final in the base class, but the box.net plugin needs to override it for dependent export config that&#039;s a bit funky.&lt;br /&gt;
&lt;br /&gt;
==Database tables==&lt;br /&gt;
In case we need to have a database table that holds some specific information used for the portfolio plugin, we will need to create the file &#039;&#039;/portfolio/foo/lib/&#039;&#039;&#039;install.xml&#039;&#039;&#039;&#039;&#039; with the table schema contained within it.&lt;br /&gt;
&lt;br /&gt;
To create the install.xml file, use the [[XMLDB editor]]. See [[Database_FAQ#XMLDB|Database FAQ &amp;gt; XMLDB]] for further details.&lt;br /&gt;
&lt;br /&gt;
Up-to-date documentation on upgrading portfolio plugin, as well as providing new capabilities and events to the system, can be found under [https://docs.moodle.org/dev/Installing_and_upgrading_plugin_database_tables#install.php Installing and Upgrading Plugin Database Tables]&lt;br /&gt;
&lt;br /&gt;
[[Category:Portfolios]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31883</id>
		<title>Portfolio plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31883"/>
		<updated>2012-01-27T09:00:42Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).&lt;br /&gt;
&lt;br /&gt;
===Error handling===&lt;br /&gt;
Your code should always throw exceptions of type portfolio_plugin_exception.  You should NOT call error or print_error or whatever, and also any third party exceptions should really be caught and rethrown although most of the time other exceptions will be caught too by the portfolio code (although this will trigger a warning in debug mode)&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
If your plugin isn&#039;t going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/. &lt;br /&gt;
&lt;br /&gt;
===File access===&lt;br /&gt;
&#039;&#039;&#039;All&#039;&#039;&#039; of the handling of files using the Files API should go through the portfolio exporter object.  There are various helper methods you can use (see prepare_package documentation) in the exporter object.  The reason for this is that this object is mocked during unit tests so that files don&#039;t actually get moved around while tests are running.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
==Template==&lt;br /&gt;
Please refer to portfolio goodledocs plugins ([http://git.moodle.org/gw?p=moodle.git;a=tree;f=portfolio/googledocs;h=6d5e67e99545dc7430df5ad065d4d5da8945417d;hb=master portfolio/googledocs/]) as template reference to start with.&lt;br /&gt;
&lt;br /&gt;
==Naming convention==&lt;br /&gt;
&lt;br /&gt;
Plugin class name should start with portfolio_plugin_&#039;&#039;&#039;&#039;&#039;PLUGINNAME&#039;&#039;&#039;&#039;&#039;. This class should also extends from &#039;&#039;&#039;portfolio_plugin_push_base&#039;&#039;&#039; or &#039;&#039;&#039;portfolio_plugin_pull_base&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class portfolio_plugin_foo extends portfolio_plugin_push_base {}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
For more information regarding naming convention, please refer to: [[Frankenstyle|Frankenstyle page]]&lt;br /&gt;
&lt;br /&gt;
==File structure==&lt;br /&gt;
For the purposes of this tutorial, it will be assumed you want to create a plugin called &amp;quot;&#039;&#039;foo&#039;&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Create a new directory in portfolio/ for the new plugin.  The name for this directory should refer to plugin&#039;s name, for example: portfolio&#039;&#039;/foo&#039;&#039;.&lt;br /&gt;
# Create the standard [[version.php]] in here that you would normally create for most other plugin types in Moodle.&lt;br /&gt;
# Create a &#039;&#039;lib.php&#039;&#039; inside the plugin directory, for example: portfolio/foo&#039;&#039;&#039;/lib.php&#039;&#039;&#039;.&lt;br /&gt;
# Create the necessary subclass to make our plugin work.  The subclass must be called portfolio_plugin&#039;&#039;&#039;&#039;&#039;_PLUGINNAME&#039;&#039;&#039;&#039;&#039; (eg: portfolio_plugin_foo) and directly extend either &#039;&#039;portfolio_plugin_push_base&#039;&#039; or &#039;&#039;portfolio_plugin_pull_base&#039;&#039;.   The only real differences between them are that one pushes the package directly to the remote system (usually via a HTTP POST, but could be filesystem based), and the other type (pull) requires the remote system to request it.&lt;br /&gt;
# Create file for plugin language strings:&lt;br /&gt;
## create directory &#039;&#039;&#039;lang/&#039;&#039;&#039; and subdirectory &#039;&#039;&#039;$language/&#039;&#039;&#039; within the plugin, for example: portfolio/foo&#039;&#039;&#039;/lang/$language/&#039;&#039;&#039;.  English (en) language must exist as this language will be use as default language string. &lt;br /&gt;
## create php file using this naming convention, portfolio_&#039;&#039;&#039;PLUGINNAME&#039;&#039;&#039;.php, for example: /portfolio/foo/lang/en/&#039;&#039;&#039;portfolio_foo.php&#039;&#039;&#039;&lt;br /&gt;
# Optionally, this plugin could store some information in database.  In order to store data to database, create &#039;&#039;&#039;db/&#039;&#039;&#039; directory within the plugin directory, for example: portfolio/foo/&#039;&#039;&#039;db/&#039;&#039;&#039;.  Also, create &#039;&#039;&#039;install.xml&#039;&#039;&#039; for the table schema.  For further details using database table, please refer to: [[XMLDB editor]] and  [[Database_FAQ#XMLDB|Database FAQ &amp;gt; XMLDB]].&lt;br /&gt;
Read the phpdoc documentation for the base classes for further information about each function, including the arguments it takes and the expected return type.  The following documentation serves as an overview.&lt;br /&gt;
&lt;br /&gt;
==Interfacing to APIs==&lt;br /&gt;
&lt;br /&gt;
===Methods you must override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====get_name=====&lt;br /&gt;
&lt;br /&gt;
Return a localized name for this plugin. Usually just something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;pluginname&#039;, &#039;portfolio_yourplugin&#039;); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is enforced as an abstract function rather than a loose fuzzy dependence on a language string for maximum ease when developing a new plugin (it&#039;s blindly obvious you must implement it ;) )&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====prepare_package=====&lt;br /&gt;
&lt;br /&gt;
Does anything necessary to prepare the package for sending.  This might be writing out a metadata manifest file, or zipping up all the files in the temporary directory.  This is called after the corresponding prepare_package method in the caller, which writes files out to a temporary location.  Files can then be retrieved from there using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles, which returns an array of stored_file objects.  You can zip files using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;zip_tempfiles.  You &#039;&#039;&#039;must&#039;&#039;&#039; not use the files api directly.&lt;br /&gt;
&lt;br /&gt;
=====send_package=====&lt;br /&gt;
&lt;br /&gt;
Actually send the package to the remote system (this might be just sending an xmlrpc request to say a file is ready for the remote system to fetch, (in which case you must set the $file member variable for portfolio/file.php later, or actually transfering the file). You can retrieve the files the caller has written out using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles() which returns an array of stored_file objects.&lt;br /&gt;
&lt;br /&gt;
=====get_interactive_continue_url=====&lt;br /&gt;
&lt;br /&gt;
Return a url to present to the user as a &#039;continue to their portfolio&#039; link.  This can return false, although then you might want to implement get_extra_finish_options.   See also get_static_continue_url and resolve_static_continue_url&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====verify_file_request_params (pull only)=====&lt;br /&gt;
&lt;br /&gt;
When remote systems request files, access control check is delegated to the plugin, by passing the array of request parameters. Should this function return false, access to the file will be denied.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====expected_time=====&lt;br /&gt;
&lt;br /&gt;
How long a transfer can reasonably expect to take.  This function is passed the estimate from the caller (which usually takes into account filesize) and can either agree with it by returning whatever it is given, or override it.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH. 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;
&lt;br /&gt;
===Methods you can override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====plugin_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
If the plugin depends on some other part of Moodle being configured in a particular way (for example if your plugin relies on using mnet for transport and mnet is off), you can override this function.  It is called frequently in different places in the code, and whenever a non empty value is returned (an error code), all instances of your plugin will be set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====has_admin_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has some admininistrative configuration settings (rather than by the user), override this plugin to return true. You must also override admin_config_form and get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_config=====&lt;br /&gt;
&lt;br /&gt;
Because most of the handling of the getting and setting of admin entered configuration data happens in the parent class, with no need to be overridden in the subclasses, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====allows_multiple_instances=====&lt;br /&gt;
&lt;br /&gt;
By default, all plugins can have multiple instances configured.  If it&#039;s only sensible for your plugin to be able to have one instance configured, override this to return false.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
=====supported_formats=====&lt;br /&gt;
The formats this plugin 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 parent class defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
=====has_user_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can be configured in the user profile section, override this function to return true. You must also override user_config_form and get_allowed_user_config.  You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====has_export_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can have further (user) configuration during export, override this function to return true. You must also override export_config_form, get_export_summary and get_allowed_export_config.  You can additionally override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_form=====&lt;br /&gt;
&lt;br /&gt;
This function can actually be called statically or non statically, depending on whether it&#039;s for editing an existing instance of the plugin, or for creating a new one.  It&#039;s passed an mform object by reference, as are the other two config form functions, to add elements to it.  If you override this you don&#039;t need to handle setting the data of the elements (when editing), that&#039;s done in the caller, using get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====user_config_form=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_user_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.   You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====export_config_form=====&lt;br /&gt;
    &lt;br /&gt;
If your plugin has overridden has_export_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.  You can also override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_validation=====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object. This function can be called statically or non statically.&lt;br /&gt;
&lt;br /&gt;
=====user_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;
=====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_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 an array of config options, the keys being nice strings to display to the user and the values being the selected config values.&lt;br /&gt;
&lt;br /&gt;
=====steal_control=====&lt;br /&gt;
&lt;br /&gt;
During any part of the export process, a plugin can completely steal control away from portfolio/add.php. This is useful, for example, for plugins that need the user go to log into a remote system and grant an application access.  It could be also used for a completely custom screen provided by the plugin.  If you need this, override this function to return a url, and the user will be redirected there.  When you&#039;re finished, return to $CFG-&amp;gt;wwwroot/portfolio/add.php?postcontrol=1 and processing will continue.   If you override this, it might be useful to also override post_control.&lt;br /&gt;
&lt;br /&gt;
=====post_control=====&lt;br /&gt;
&lt;br /&gt;
After control is returned after steal_control, post_control will be called before the next stage is processed, and passed any request parameters.  For an example of how this is used, see the box.net plugin, which uses steal_control to redirect to box.net to get the user to authenticate and then box.net redirects back to a url passing an authentication token to use for the rest of the session.  Since it&#039;s part of the request parameters, it&#039;s passed through to post_control, which stores whatever it needs before the next stage.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_user_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of user entered configuration data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_export_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of per-export config data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed (note that if you store non-user entered config data you must implement this function)&lt;br /&gt;
&lt;br /&gt;
=====instance_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
Similar to plugin_sanity_check although operates on an instance basis.   Again, returning anything non empty here will be taken as an error string, and the instance set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====send_file (pull only)=====&lt;br /&gt;
&lt;br /&gt;
Sends the file to STDOUT (the browser in the case of the download plugin but may be an external system requesting it) - this function gets called when portfolio/file.php is requested and by default just passes the contents of the file straight through to the browser, unencrypted.  The other pull plugin this far is mahara, and it doesn&#039;t implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.&lt;br /&gt;
&lt;br /&gt;
=====cleanup=====&lt;br /&gt;
&lt;br /&gt;
if you&#039;ve stashed anything in extra database tables, you can implement this function to clean them up.  this is called on cron to cleanup expired transfers, as well as after a successful transfer.&lt;br /&gt;
&lt;br /&gt;
=====mnet_publishes=====&lt;br /&gt;
&lt;br /&gt;
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)&lt;br /&gt;
&lt;br /&gt;
=====get_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
In some cases, the transfer is logged from somewhere other than the interactive browser session (eg when a pull plugin completes the transfer before the browser is redirected to the finish page). The static_continue_url is the one that is finally logged and inserted into the log table, and used to display later to the user (in the Transfer log screen under portfolios in their profile).  This, combined with resolve_static_continue_url, are used to generate a new url later to the user.&lt;br /&gt;
&lt;br /&gt;
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid.&lt;br /&gt;
The static continue url just contains the &amp;quot;wantsurl&amp;quot; part of that in mahara - eg artefact/file/&lt;br /&gt;
resolve_static_continue_url converts the static url into a new mnet jump session url.&lt;br /&gt;
&lt;br /&gt;
=====resolve_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
See get_static_continue_url.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Methods you shouldn&#039;t really override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====create_instance=====&lt;br /&gt;
&lt;br /&gt;
Creates an instance of the given plugin.  As well as plugin and name, this can also be passed initial admin config. Returns an object of the correct subclass.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====__construct=====&lt;br /&gt;
&lt;br /&gt;
Constructor for the plugin.  Subclasses don&#039;t need to override this.&lt;br /&gt;
&lt;br /&gt;
=====save=====&lt;br /&gt;
&lt;br /&gt;
Saves data stored in the object back to the database and resets the dirty flag.&lt;br /&gt;
&lt;br /&gt;
=====delete=====&lt;br /&gt;
&lt;br /&gt;
Deletes this instance and all configuration data associated with it completely from the database&lt;br /&gt;
&lt;br /&gt;
=====set_export_config=====&lt;br /&gt;
&lt;br /&gt;
I had originally made this function final in the base class, but the box.net plugin needs to override it for dependent export config that&#039;s a bit funky.&lt;br /&gt;
&lt;br /&gt;
==Database tables==&lt;br /&gt;
In case we need to have a database table that holds some specific information used for the portfolio plugin, we will need to create the file &#039;&#039;/portfolio/foo/lib/&#039;&#039;&#039;install.xml&#039;&#039;&#039;&#039;&#039; with the table schema contained within it.&lt;br /&gt;
&lt;br /&gt;
To create the install.xml file, use the [[XMLDB editor]]. See [[Database_FAQ#XMLDB|Database FAQ &amp;gt; XMLDB]] for further details.&lt;br /&gt;
&lt;br /&gt;
Up-to-date documentation on upgrading portfolio plugin, as well as providing new capabilities and events to the system, can be found under [https://docs.moodle.org/dev/Installing_and_upgrading_plugin_database_tables#install.php Installing and Upgrading Plugin Database Tables]&lt;br /&gt;
&lt;br /&gt;
[[Category:Portfolios]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31871</id>
		<title>Portfolio plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31871"/>
		<updated>2012-01-27T06:32:42Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).&lt;br /&gt;
&lt;br /&gt;
===Error handling===&lt;br /&gt;
Your code should always throw exceptions of type portfolio_plugin_exception.  You should NOT call error or print_error or whatever, and also any third party exceptions should really be caught and rethrown although most of the time other exceptions will be caught too by the portfolio code (although this will trigger a warning in debug mode)&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
If your plugin isn&#039;t going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/. &lt;br /&gt;
&lt;br /&gt;
===File access===&lt;br /&gt;
&#039;&#039;&#039;All&#039;&#039;&#039; of the handling of files using the Files API should go through the portfolio exporter object.  There are various helper methods you can use (see prepare_package documentation) in the exporter object.  The reason for this is that this object is mocked during unit tests so that files don&#039;t actually get moved around while tests are running.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
==Template==&lt;br /&gt;
Please refer to portfolio goodledocs plugins ([http://git.moodle.org/gw?p=moodle.git;a=tree;f=portfolio/googledocs;h=6d5e67e99545dc7430df5ad065d4d5da8945417d;hb=master portfolio/googledocs/]) as template reference to start with.&lt;br /&gt;
&lt;br /&gt;
==Naming convention==&lt;br /&gt;
&lt;br /&gt;
Plugin class name should start with portfolio_plugin_&#039;&#039;&#039;&#039;&#039;PLUGINNAME&#039;&#039;&#039;&#039;&#039;. This class should also extends from &#039;&#039;&#039;portfolio_plugin_push_base&#039;&#039;&#039; or &#039;&#039;&#039;portfolio_plugin_pull_base&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class portfolio_plugin_foo extends portfolio_plugin_push_base {}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
For more information regarding naming convention, please refer to: [[Frankenstyle|Frankenstyle page]]&lt;br /&gt;
&lt;br /&gt;
==File structure==&lt;br /&gt;
For the purposes of this tutorial, it will be assumed you want to create a plugin called &amp;quot;&#039;&#039;foo&#039;&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Create a new directory in portfolio/ for the new plugin.  The name for this directory should refer to plugin&#039;s name, for example: portfolio&#039;&#039;/foo&#039;&#039;.&lt;br /&gt;
# Create the standard [[version.php]] in here that you would normally create for most other plugin types in Moodle.&lt;br /&gt;
# Create a &#039;&#039;lib.php&#039;&#039; inside the plugin directory, for example: portfolio/foo&#039;&#039;&#039;/lib.php&#039;&#039;&#039;.&lt;br /&gt;
# Create the necessary subclass to make our plugin work.  The subclass must be called portfolio_plugin&#039;&#039;&#039;&#039;&#039;_PLUGINNAME&#039;&#039;&#039;&#039;&#039; (eg: portfolio_plugin_foo) and directly extend either &#039;&#039;portfolio_plugin_push_base&#039;&#039; or &#039;&#039;portfolio_plugin_pull_base&#039;&#039;.   The only real differences between them are that one pushes the package directly to the remote system (usually via a HTTP POST, but could be filesystem based), and the other type (pull) requires the remote system to request it.&lt;br /&gt;
# Create file for plugin language strings:&lt;br /&gt;
## create directory &#039;&#039;&#039;lang/&#039;&#039;&#039; and subdirectory &#039;&#039;&#039;$language/&#039;&#039;&#039; within the plugin, for example: portfolio/foo&#039;&#039;&#039;/lang/$language/&#039;&#039;&#039;.  English (en) language must exist as this language will be use as default language string. &lt;br /&gt;
## create php file using this naming convention, portfolio_&#039;&#039;&#039;PLUGINNAME&#039;&#039;&#039;.php, for example: /portfolio/foo/lang/en/&#039;&#039;&#039;portfolio_foo.php&#039;&#039;&#039;&lt;br /&gt;
# Optionally, this plugin could store some information in database.  In order to store data to database, create &#039;&#039;&#039;db/&#039;&#039;&#039; directory within the plugin directory, for example: portfolio/foo/&#039;&#039;&#039;db/&#039;&#039;&#039;.  Also, create &#039;&#039;&#039;install.xml&#039;&#039;&#039; for the table schema.  For further details using database table, please refer to: [[XMLDB editor]] and  [[Database_FAQ#XMLDB|Database FAQ &amp;gt; XMLDB]].&lt;br /&gt;
Read the phpdoc documentation for the base classes for further information about each function, including the arguments it takes and the expected return type.  The following documentation serves as an overview.&lt;br /&gt;
&lt;br /&gt;
==Interfacing to APIs==&lt;br /&gt;
&lt;br /&gt;
===Methods you must override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====get_name=====&lt;br /&gt;
&lt;br /&gt;
Return a localized name for this plugin. Usually just something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;pluginname&#039;, &#039;portfolio_yourplugin&#039;); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is enforced as an abstract function rather than a loose fuzzy dependence on a language string for maximum ease when developing a new plugin (it&#039;s blindly obvious you must implement it ;) )&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====prepare_package=====&lt;br /&gt;
&lt;br /&gt;
Does anything necessary to prepare the package for sending.  This might be writing out a metadata manifest file, or zipping up all the files in the temporary directory.  This is called after the corresponding prepare_package method in the caller, which writes files out to a temporary location.  Files can then be retrieved from there using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles, which returns an array of stored_file objects.  You can zip files using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;zip_tempfiles.  You &#039;&#039;&#039;must&#039;&#039;&#039; not use the files api directly.&lt;br /&gt;
&lt;br /&gt;
=====send_package=====&lt;br /&gt;
&lt;br /&gt;
Actually send the package to the remote system (this might be just sending an xmlrpc request to say a file is ready for the remote system to fetch, (in which case you must set the $file member variable for portfolio/file.php later, or actually transfering the file). You can retrieve the files the caller has written out using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles() which returns an array of stored_file objects.&lt;br /&gt;
&lt;br /&gt;
=====get_interactive_continue_url=====&lt;br /&gt;
&lt;br /&gt;
Return a url to present to the user as a &#039;continue to their portfolio&#039; link.  This can return false, although then you might want to implement get_extra_finish_options.   See also get_static_continue_url and resolve_static_continue_url&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====verify_file_request_params (pull only)=====&lt;br /&gt;
&lt;br /&gt;
When remote systems request files, access control check is delegated to the plugin, by passing the array of request parameters. Should this function return false, access to the file will be denied.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====expected_time=====&lt;br /&gt;
&lt;br /&gt;
How long a transfer can reasonably expect to take.  This function is passed the estimate from the caller (which usually takes into account filesize) and can either agree with it by returning whatever it is given, or override it.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH. 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;
&lt;br /&gt;
===Methods you can override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====supported_formats=====&lt;br /&gt;
&lt;br /&gt;
The formats this plugin 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 parent class defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
=====plugin_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
If the plugin depends on some other part of Moodle being configured in a particular way (for example if your plugin relies on using mnet for transport and mnet is off), you can override this function.  It is called frequently in different places in the code, and whenever a non empty value is returned (an error code), all instances of your plugin will be set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====has_admin_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has some admininistrative configuration settings (rather than by the user), override this plugin to return true. You must also override admin_config_form and get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_config=====&lt;br /&gt;
&lt;br /&gt;
Because most of the handling of the getting and setting of admin entered configuration data happens in the parent class, with no need to be overridden in the subclasses, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====allows_multiple_instances=====&lt;br /&gt;
&lt;br /&gt;
By default, all plugins can have multiple instances configured.  If it&#039;s only sensible for your plugin to be able to have one instance configured, override this to return false.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====has_user_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can be configured in the user profile section, override this function to return true. You must also override user_config_form and get_allowed_user_config.  You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====has_export_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can have further (user) configuration during export, override this function to return true. You must also override export_config_form, get_export_summary and get_allowed_export_config.  You can additionally override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_form=====&lt;br /&gt;
&lt;br /&gt;
This function can actually be called statically or non statically, depending on whether it&#039;s for editing an existing instance of the plugin, or for creating a new one.  It&#039;s passed an mform object by reference, as are the other two config form functions, to add elements to it.  If you override this you don&#039;t need to handle setting the data of the elements (when editing), that&#039;s done in the caller, using get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====user_config_form=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_user_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.   You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====export_config_form=====&lt;br /&gt;
    &lt;br /&gt;
If your plugin has overridden has_export_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.  You can also override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_validation=====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object. This function can be called statically or non statically.&lt;br /&gt;
&lt;br /&gt;
=====user_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;
=====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_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 an array of config options, the keys being nice strings to display to the user and the values being the selected config values.&lt;br /&gt;
&lt;br /&gt;
=====steal_control=====&lt;br /&gt;
&lt;br /&gt;
During any part of the export process, a plugin can completely steal control away from portfolio/add.php. This is useful, for example, for plugins that need the user go to log into a remote system and grant an application access.  It could be also used for a completely custom screen provided by the plugin.  If you need this, override this function to return a url, and the user will be redirected there.  When you&#039;re finished, return to $CFG-&amp;gt;wwwroot/portfolio/add.php?postcontrol=1 and processing will continue.   If you override this, it might be useful to also override post_control.&lt;br /&gt;
&lt;br /&gt;
=====post_control=====&lt;br /&gt;
&lt;br /&gt;
After control is returned after steal_control, post_control will be called before the next stage is processed, and passed any request parameters.  For an example of how this is used, see the box.net plugin, which uses steal_control to redirect to box.net to get the user to authenticate and then box.net redirects back to a url passing an authentication token to use for the rest of the session.  Since it&#039;s part of the request parameters, it&#039;s passed through to post_control, which stores whatever it needs before the next stage.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_user_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of user entered configuration data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_export_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of per-export config data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed (note that if you store non-user entered config data you must implement this function)&lt;br /&gt;
&lt;br /&gt;
=====instance_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
Similar to plugin_sanity_check although operates on an instance basis.   Again, returning anything non empty here will be taken as an error string, and the instance set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====send_file (pull only)=====&lt;br /&gt;
&lt;br /&gt;
Sends the file to STDOUT (the browser in the case of the download plugin but may be an external system requesting it) - this function gets called when portfolio/file.php is requested and by default just passes the contents of the file straight through to the browser, unencrypted.  The other pull plugin this far is mahara, and it doesn&#039;t implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.&lt;br /&gt;
&lt;br /&gt;
=====cleanup=====&lt;br /&gt;
&lt;br /&gt;
if you&#039;ve stashed anything in extra database tables, you can implement this function to clean them up.  this is called on cron to cleanup expired transfers, as well as after a successful transfer.&lt;br /&gt;
&lt;br /&gt;
=====mnet_publishes=====&lt;br /&gt;
&lt;br /&gt;
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)&lt;br /&gt;
&lt;br /&gt;
=====get_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
In some cases, the transfer is logged from somewhere other than the interactive browser session (eg when a pull plugin completes the transfer before the browser is redirected to the finish page). The static_continue_url is the one that is finally logged and inserted into the log table, and used to display later to the user (in the Transfer log screen under portfolios in their profile).  This, combined with resolve_static_continue_url, are used to generate a new url later to the user.&lt;br /&gt;
&lt;br /&gt;
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid.&lt;br /&gt;
The static continue url just contains the &amp;quot;wantsurl&amp;quot; part of that in mahara - eg artefact/file/&lt;br /&gt;
resolve_static_continue_url converts the static url into a new mnet jump session url.&lt;br /&gt;
&lt;br /&gt;
=====resolve_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
See get_static_continue_url.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Methods you shouldn&#039;t really override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====create_instance=====&lt;br /&gt;
&lt;br /&gt;
Creates an instance of the given plugin.  As well as plugin and name, this can also be passed initial admin config. Returns an object of the correct subclass.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====__construct=====&lt;br /&gt;
&lt;br /&gt;
Constructor for the plugin.  Subclasses don&#039;t need to override this.&lt;br /&gt;
&lt;br /&gt;
=====save=====&lt;br /&gt;
&lt;br /&gt;
Saves data stored in the object back to the database and resets the dirty flag.&lt;br /&gt;
&lt;br /&gt;
=====delete=====&lt;br /&gt;
&lt;br /&gt;
Deletes this instance and all configuration data associated with it completely from the database&lt;br /&gt;
&lt;br /&gt;
=====set_export_config=====&lt;br /&gt;
&lt;br /&gt;
I had originally made this function final in the base class, but the box.net plugin needs to override it for dependent export config that&#039;s a bit funky.&lt;br /&gt;
&lt;br /&gt;
==Database tables==&lt;br /&gt;
In case we need to have a database table that holds some specific information used for the portfolio plugin, we will need to create the file &#039;&#039;/portfolio/foo/lib/&#039;&#039;&#039;install.xml&#039;&#039;&#039;&#039;&#039; with the table schema contained within it.&lt;br /&gt;
&lt;br /&gt;
To create the install.xml file, use the [[XMLDB editor]]. See [[Database_FAQ#XMLDB|Database FAQ &amp;gt; XMLDB]] for further details.&lt;br /&gt;
&lt;br /&gt;
Up-to-date documentation on upgrading portfolio plugin, as well as providing new capabilities and events to the system, can be found under [https://docs.moodle.org/dev/Installing_and_upgrading_plugin_database_tables#install.php Installing and Upgrading Plugin Database Tables]&lt;br /&gt;
&lt;br /&gt;
[[Category:Portfolios]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31868</id>
		<title>Portfolio plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31868"/>
		<updated>2012-01-27T06:05:29Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).&lt;br /&gt;
&lt;br /&gt;
===Error handling===&lt;br /&gt;
Your code should always throw exceptions of type portfolio_plugin_exception.  You should NOT call error or print_error or whatever, and also any third party exceptions should really be caught and rethrown although most of the time other exceptions will be caught too by the portfolio code (although this will trigger a warning in debug mode)&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
If your plugin isn&#039;t going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/. &lt;br /&gt;
&lt;br /&gt;
===File access===&lt;br /&gt;
&#039;&#039;&#039;All&#039;&#039;&#039; of the handling of files using the Files API should go through the portfolio exporter object.  There are various helper methods you can use (see prepare_package documentation) in the exporter object.  The reason for this is that this object is mocked during unit tests so that files don&#039;t actually get moved around while tests are running.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
==Template==&lt;br /&gt;
Please refer to portfolio goodledocs plugins ([http://git.moodle.org/gw?p=moodle.git;a=tree;f=portfolio/googledocs;h=6d5e67e99545dc7430df5ad065d4d5da8945417d;hb=master portfolio/googledocs/]) as template to start with.&lt;br /&gt;
&lt;br /&gt;
==Naming convention==&lt;br /&gt;
&lt;br /&gt;
Plugin class name should start with portfolio_plugin_&#039;&#039;&#039;&#039;&#039;PLUGINNAME&#039;&#039;&#039;&#039;&#039;. This class should also extends from &#039;&#039;&#039;portfolio_plugin_push_base&#039;&#039;&#039; or &#039;&#039;&#039;portfolio_plugin_pull_base&#039;&#039;&#039;.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class portfolio_plugin_foo extends portfolio_plugin_push_base {}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
For more information regarding naming convention, please refer to: [[Frankenstyle|Frankenstyle page]]&lt;br /&gt;
&lt;br /&gt;
==File structure==&lt;br /&gt;
For the purposes of this tutorial, it will be assumed you want to create a plugin called &amp;quot;&#039;&#039;foo&#039;&#039;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Create a new directory in portfolio/ for the new plugin.  The name for this directory should refer to plugin&#039;s name, for example: portfolio&#039;&#039;/foo&#039;&#039;.&lt;br /&gt;
# Create the standard [[version.php]] in here that you would normally create for most other plugin types in Moodle.&lt;br /&gt;
# Create a &#039;&#039;lib.php&#039;&#039; inside the plugin directory, for example: portfolio/foo&#039;&#039;&#039;/lib.php&#039;&#039;&#039;.&lt;br /&gt;
# Create the necessary subclass to make our plugin work.  The subclass must be called portfolio_plugin&#039;&#039;&#039;&#039;&#039;_PLUGINNAME&#039;&#039;&#039;&#039;&#039; (eg: portfolio_plugin_foo) and directly extend either &#039;&#039;portfolio_plugin_push_base&#039;&#039; or &#039;&#039;portfolio_plugin_pull_base&#039;&#039;.   The only real differences between them are that one pushes the package directly to the remote system (usually via a HTTP POST, but could be filesystem based), and the other type (pull) requires the remote system to request it.&lt;br /&gt;
# Create file for plugin language strings:&lt;br /&gt;
## create directory &#039;&#039;&#039;lang/&#039;&#039;&#039; and subdirectory &#039;&#039;&#039;$language/&#039;&#039;&#039; within the plugin, for example: portfolio/foo&#039;&#039;&#039;/lang/$language/&#039;&#039;&#039;.  English (en) language must exist as this language will be use as default language string. &lt;br /&gt;
## create php file using this naming convention, portfolio_&#039;&#039;&#039;PLUGINNAME&#039;&#039;&#039;.php, for example: /portfolio/foo/lang/en/&#039;&#039;&#039;portfolio_foo.php&#039;&#039;&#039;&lt;br /&gt;
# Optionally, this plugin could store some information in database.  In order to store data to database, create &#039;&#039;&#039;db/&#039;&#039;&#039; directory within the plugin directory, for example: portfolio/foo/&#039;&#039;&#039;db/&#039;&#039;&#039;.  Also, create &#039;&#039;&#039;install.xml&#039;&#039;&#039; for the table schema.  For further details using database table, please refer to: [[XMLDB editor]] and  [[Database_FAQ#XMLDB|Database FAQ &amp;gt; XMLDB]].&lt;br /&gt;
Read the phpdoc documentation for the base classes for further information about each function, including the arguments it takes and the expected return type.  The following documentation serves as an overview.&lt;br /&gt;
&lt;br /&gt;
==Interfacing to APIs==&lt;br /&gt;
&lt;br /&gt;
===Methods you must override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====get_name=====&lt;br /&gt;
&lt;br /&gt;
Return a localized name for this plugin. Usually just something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;pluginname&#039;, &#039;portfolio_yourplugin&#039;); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is enforced as an abstract function rather than a loose fuzzy dependence on a language string for maximum ease when developing a new plugin (it&#039;s blindly obvious you must implement it ;) )&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====prepare_package=====&lt;br /&gt;
&lt;br /&gt;
Does anything necessary to prepare the package for sending.  This might be writing out a metadata manifest file, or zipping up all the files in the temporary directory.  This is called after the corresponding prepare_package method in the caller, which writes files out to a temporary location.  Files can then be retrieved from there using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles, which returns an array of stored_file objects.  You can zip files using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;zip_tempfiles.  You &#039;&#039;&#039;must&#039;&#039;&#039; not use the files api directly.&lt;br /&gt;
&lt;br /&gt;
=====send_package=====&lt;br /&gt;
&lt;br /&gt;
Actually send the package to the remote system (this might be just sending an xmlrpc request to say a file is ready for the remote system to fetch, (in which case you must set the $file member variable for portfolio/file.php later, or actually transfering the file). You can retrieve the files the caller has written out using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles() which returns an array of stored_file objects.&lt;br /&gt;
&lt;br /&gt;
=====get_interactive_continue_url=====&lt;br /&gt;
&lt;br /&gt;
Return a url to present to the user as a &#039;continue to their portfolio&#039; link.  This can return false, although then you might want to implement get_extra_finish_options.   See also get_static_continue_url and resolve_static_continue_url&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====verify_file_request_params (pull only)=====&lt;br /&gt;
&lt;br /&gt;
When remote systems request files, access control check is delegated to the plugin, by passing the array of request parameters. Should this function return false, access to the file will be denied.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====expected_time=====&lt;br /&gt;
&lt;br /&gt;
How long a transfer can reasonably expect to take.  This function is passed the estimate from the caller (which usually takes into account filesize) and can either agree with it by returning whatever it is given, or override it.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH. 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;
&lt;br /&gt;
===Methods you can override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====supported_formats=====&lt;br /&gt;
&lt;br /&gt;
The formats this plugin 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 parent class defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
=====plugin_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
If the plugin depends on some other part of Moodle being configured in a particular way (for example if your plugin relies on using mnet for transport and mnet is off), you can override this function.  It is called frequently in different places in the code, and whenever a non empty value is returned (an error code), all instances of your plugin will be set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====has_admin_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has some admininistrative configuration settings (rather than by the user), override this plugin to return true. You must also override admin_config_form and get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_config=====&lt;br /&gt;
&lt;br /&gt;
Because most of the handling of the getting and setting of admin entered configuration data happens in the parent class, with no need to be overridden in the subclasses, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====allows_multiple_instances=====&lt;br /&gt;
&lt;br /&gt;
By default, all plugins can have multiple instances configured.  If it&#039;s only sensible for your plugin to be able to have one instance configured, override this to return false.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====has_user_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can be configured in the user profile section, override this function to return true. You must also override user_config_form and get_allowed_user_config.  You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====has_export_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can have further (user) configuration during export, override this function to return true. You must also override export_config_form, get_export_summary and get_allowed_export_config.  You can additionally override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_form=====&lt;br /&gt;
&lt;br /&gt;
This function can actually be called statically or non statically, depending on whether it&#039;s for editing an existing instance of the plugin, or for creating a new one.  It&#039;s passed an mform object by reference, as are the other two config form functions, to add elements to it.  If you override this you don&#039;t need to handle setting the data of the elements (when editing), that&#039;s done in the caller, using get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====user_config_form=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_user_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.   You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====export_config_form=====&lt;br /&gt;
    &lt;br /&gt;
If your plugin has overridden has_export_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.  You can also override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_validation=====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object. This function can be called statically or non statically.&lt;br /&gt;
&lt;br /&gt;
=====user_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;
=====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_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 an array of config options, the keys being nice strings to display to the user and the values being the selected config values.&lt;br /&gt;
&lt;br /&gt;
=====steal_control=====&lt;br /&gt;
&lt;br /&gt;
During any part of the export process, a plugin can completely steal control away from portfolio/add.php. This is useful, for example, for plugins that need the user go to log into a remote system and grant an application access.  It could be also used for a completely custom screen provided by the plugin.  If you need this, override this function to return a url, and the user will be redirected there.  When you&#039;re finished, return to $CFG-&amp;gt;wwwroot/portfolio/add.php?postcontrol=1 and processing will continue.   If you override this, it might be useful to also override post_control.&lt;br /&gt;
&lt;br /&gt;
=====post_control=====&lt;br /&gt;
&lt;br /&gt;
After control is returned after steal_control, post_control will be called before the next stage is processed, and passed any request parameters.  For an example of how this is used, see the box.net plugin, which uses steal_control to redirect to box.net to get the user to authenticate and then box.net redirects back to a url passing an authentication token to use for the rest of the session.  Since it&#039;s part of the request parameters, it&#039;s passed through to post_control, which stores whatever it needs before the next stage.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_user_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of user entered configuration data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_export_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of per-export config data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed (note that if you store non-user entered config data you must implement this function)&lt;br /&gt;
&lt;br /&gt;
=====instance_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
Similar to plugin_sanity_check although operates on an instance basis.   Again, returning anything non empty here will be taken as an error string, and the instance set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====send_file (pull only)=====&lt;br /&gt;
&lt;br /&gt;
Sends the file to STDOUT (the browser in the case of the download plugin but may be an external system requesting it) - this function gets called when portfolio/file.php is requested and by default just passes the contents of the file straight through to the browser, unencrypted.  The other pull plugin this far is mahara, and it doesn&#039;t implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.&lt;br /&gt;
&lt;br /&gt;
=====cleanup=====&lt;br /&gt;
&lt;br /&gt;
if you&#039;ve stashed anything in extra database tables, you can implement this function to clean them up.  this is called on cron to cleanup expired transfers, as well as after a successful transfer.&lt;br /&gt;
&lt;br /&gt;
=====mnet_publishes=====&lt;br /&gt;
&lt;br /&gt;
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)&lt;br /&gt;
&lt;br /&gt;
=====get_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
In some cases, the transfer is logged from somewhere other than the interactive browser session (eg when a pull plugin completes the transfer before the browser is redirected to the finish page). The static_continue_url is the one that is finally logged and inserted into the log table, and used to display later to the user (in the Transfer log screen under portfolios in their profile).  This, combined with resolve_static_continue_url, are used to generate a new url later to the user.&lt;br /&gt;
&lt;br /&gt;
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid.&lt;br /&gt;
The static continue url just contains the &amp;quot;wantsurl&amp;quot; part of that in mahara - eg artefact/file/&lt;br /&gt;
resolve_static_continue_url converts the static url into a new mnet jump session url.&lt;br /&gt;
&lt;br /&gt;
=====resolve_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
See get_static_continue_url.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Methods you shouldn&#039;t really override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====create_instance=====&lt;br /&gt;
&lt;br /&gt;
Creates an instance of the given plugin.  As well as plugin and name, this can also be passed initial admin config. Returns an object of the correct subclass.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====__construct=====&lt;br /&gt;
&lt;br /&gt;
Constructor for the plugin.  Subclasses don&#039;t need to override this.&lt;br /&gt;
&lt;br /&gt;
=====save=====&lt;br /&gt;
&lt;br /&gt;
Saves data stored in the object back to the database and resets the dirty flag.&lt;br /&gt;
&lt;br /&gt;
=====delete=====&lt;br /&gt;
&lt;br /&gt;
Deletes this instance and all configuration data associated with it completely from the database&lt;br /&gt;
&lt;br /&gt;
=====set_export_config=====&lt;br /&gt;
&lt;br /&gt;
I had originally made this function final in the base class, but the box.net plugin needs to override it for dependent export config that&#039;s a bit funky.&lt;br /&gt;
&lt;br /&gt;
==Database tables==&lt;br /&gt;
In case we need to have a database table that holds some specific information used for the portfolio plugin, we will need to create the file &#039;&#039;/portfolio/foo/lib/&#039;&#039;&#039;install.xml&#039;&#039;&#039;&#039;&#039; with the table schema contained within it.&lt;br /&gt;
&lt;br /&gt;
To create the install.xml file, use the [[XMLDB editor]]. See [[Database_FAQ#XMLDB|Database FAQ &amp;gt; XMLDB]] for further details.&lt;br /&gt;
&lt;br /&gt;
Up-to-date documentation on upgrading portfolio plugin, as well as providing new capabilities and events to the system, can be found under [https://docs.moodle.org/dev/Installing_and_upgrading_plugin_database_tables#install.php Installing and Upgrading Plugin Database Tables]&lt;br /&gt;
&lt;br /&gt;
[[Category:Portfolios]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31820</id>
		<title>Portfolio plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31820"/>
		<updated>2012-01-27T03:31:47Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).&lt;br /&gt;
&lt;br /&gt;
===Error handling===&lt;br /&gt;
Your code should always throw exceptions of type portfolio_plugin_exception.  You should NOT call error or print_error or whatever, and also any third party exceptions should really be caught and rethrown although most of the time other exceptions will be caught too by the portfolio code (although this will trigger a warning in debug mode)&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
&lt;br /&gt;
If your plugin isn&#039;t going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/. &lt;br /&gt;
Help files behave a little differently, you need to make portfolio/type/foo/lang/$language/help/foo/helpfile.html (this surprised me).&lt;br /&gt;
&lt;br /&gt;
Both normal strings and helpfiles should be called using module portfolio_foo for non core plugins. For core plugins, normal strings should be called using portfolio_foo but helpstrings should be called using portfolio/&lt;br /&gt;
&lt;br /&gt;
===File access===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;All&#039;&#039;&#039; of the handling of files using the Files API should go through the portfolio exporter object.  There are various helper methods you can use (see prepare_package documentation) in the exporter object.  The reason for this is that this object is mocked during unit tests so that files don&#039;t actually get moved around while tests are running.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
==Template==&lt;br /&gt;
Please refer to portfolio goodledocs plugins ([http://git.moodle.org/gw?p=moodle.git;a=tree;f=portfolio/googledocs;h=6d5e67e99545dc7430df5ad065d4d5da8945417d;hb=master portfolio/googledocs/]) as template to start with.&lt;br /&gt;
&lt;br /&gt;
==Naming convention==&lt;br /&gt;
&lt;br /&gt;
Plugin class name should start with &amp;lt;tt&amp;gt;portfolio_plugin_xxx&amp;lt;/tt&amp;gt;, where xxx refer to the name of the plugin. This class should also extends from &amp;lt;tt&amp;gt;portfolio_plugin_push_base&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;portfolio_plugin_pull_base&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class portfolio_plugin_foo extends portfolio_plugin_push_base {}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
For more information regarding naming convention, please refer to: [[Frankenstyle|Frankenstyle page]]&lt;br /&gt;
&lt;br /&gt;
==File structure==&lt;br /&gt;
For the purposes of this tutorial, it will be assumed you want to create a plugin called &amp;quot;&amp;lt;tt&amp;gt;foo&amp;lt;/tt&amp;gt;&amp;quot;&lt;br /&gt;
&lt;br /&gt;
# Create a new directory in &amp;lt;tt&amp;gt;portfolio/&amp;lt;/tt&amp;gt; with the name of your plugin, for example, &amp;lt;tt&amp;gt;portfolio/foo&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# Create the standard [[version.php]] in here that you would normally create for most other plugin types in Moodle.&lt;br /&gt;
# Provide a &amp;lt;tt&amp;gt;db/&amp;lt;/tt&amp;gt; directory with an &amp;lt;tt&amp;gt;install.xml&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;upgrade.php&amp;lt;/tt&amp;gt; along the same lines.  These are optional.&lt;br /&gt;
# Create a &amp;lt;tt&amp;gt;lib.php&amp;lt;/tt&amp;gt; inside the plugin directory, for example: &amp;lt;tt&amp;gt;portfolio/foo/lib.php&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# Create the necessary subclass to make our plugin work.  It must be called &amp;lt;tt&amp;gt;portfolio_plugin_PLUGINNAME&amp;lt;/tt&amp;gt; and directly extend either &amp;lt;tt&amp;gt;portfolio_plugin_push_base&amp;lt;/tt&amp;gt;, or &amp;lt;tt&amp;gt;portfolio_plugin_pull_base&amp;lt;/tt&amp;gt;.   The only real differences between them are that one pushes the package directly to the remote system (usually via a HTTP POST, but could be filesystem based), and the other type (pull) requires the remote system to request it.  portfolio_plugin_pull_base adds just one extra further abstract function (see below)&lt;br /&gt;
&lt;br /&gt;
Read the phpdoc documentation for the base classes for further information about each function, including the arguments it takes and the expected return type.  The following documentation serves as an overview.&lt;br /&gt;
&lt;br /&gt;
==Interfacing to APIs==&lt;br /&gt;
&lt;br /&gt;
===Methods you must override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====get_name=====&lt;br /&gt;
&lt;br /&gt;
Return a localized name for this plugin. Usually just something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;pluginname&#039;, &#039;portfolio_yourplugin&#039;); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is enforced as an abstract function rather than a loose fuzzy dependence on a language string for maximum ease when developing a new plugin (it&#039;s blindly obvious you must implement it ;) )&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====prepare_package=====&lt;br /&gt;
&lt;br /&gt;
Does anything necessary to prepare the package for sending.  This might be writing out a metadata manifest file, or zipping up all the files in the temporary directory.  This is called after the corresponding prepare_package method in the caller, which writes files out to a temporary location.  Files can then be retrieved from there using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles, which returns an array of stored_file objects.  You can zip files using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;zip_tempfiles.  You &#039;&#039;&#039;must&#039;&#039;&#039; not use the files api directly.&lt;br /&gt;
&lt;br /&gt;
=====send_package=====&lt;br /&gt;
&lt;br /&gt;
Actually send the package to the remote system (this might be just sending an xmlrpc request to say a file is ready for the remote system to fetch, (in which case you must set the $file member variable for portfolio/file.php later, or actually transfering the file). You can retrieve the files the caller has written out using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles() which returns an array of stored_file objects.&lt;br /&gt;
&lt;br /&gt;
=====get_interactive_continue_url=====&lt;br /&gt;
&lt;br /&gt;
Return a url to present to the user as a &#039;continue to their portfolio&#039; link.  This can return false, although then you might want to implement get_extra_finish_options.   See also get_static_continue_url and resolve_static_continue_url&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====verify_file_request_params (pull only)=====&lt;br /&gt;
&lt;br /&gt;
When remote systems request files, access control check is delegated to the plugin, by passing the array of request parameters. Should this function return false, access to the file will be denied.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====expected_time=====&lt;br /&gt;
&lt;br /&gt;
How long a transfer can reasonably expect to take.  This function is passed the estimate from the caller (which usually takes into account filesize) and can either agree with it by returning whatever it is given, or override it.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH. 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;
&lt;br /&gt;
===Methods you can override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====supported_formats=====&lt;br /&gt;
&lt;br /&gt;
The formats this plugin 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 parent class defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
=====plugin_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
If the plugin depends on some other part of Moodle being configured in a particular way (for example if your plugin relies on using mnet for transport and mnet is off), you can override this function.  It is called frequently in different places in the code, and whenever a non empty value is returned (an error code), all instances of your plugin will be set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====has_admin_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has some admininistrative configuration settings (rather than by the user), override this plugin to return true. You must also override admin_config_form and get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_config=====&lt;br /&gt;
&lt;br /&gt;
Because most of the handling of the getting and setting of admin entered configuration data happens in the parent class, with no need to be overridden in the subclasses, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====allows_multiple_instances=====&lt;br /&gt;
&lt;br /&gt;
By default, all plugins can have multiple instances configured.  If it&#039;s only sensible for your plugin to be able to have one instance configured, override this to return false.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====has_user_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can be configured in the user profile section, override this function to return true. You must also override user_config_form and get_allowed_user_config.  You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====has_export_config=====&lt;br /&gt;
&lt;br /&gt;
If your plugin can have further (user) configuration during export, override this function to return true. You must also override export_config_form, get_export_summary and get_allowed_export_config.  You can additionally override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_form=====&lt;br /&gt;
&lt;br /&gt;
This function can actually be called statically or non statically, depending on whether it&#039;s for editing an existing instance of the plugin, or for creating a new one.  It&#039;s passed an mform object by reference, as are the other two config form functions, to add elements to it.  If you override this you don&#039;t need to handle setting the data of the elements (when editing), that&#039;s done in the caller, using get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====user_config_form=====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_user_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.   You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====export_config_form=====&lt;br /&gt;
    &lt;br /&gt;
If your plugin has overridden has_export_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.  You can also override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
=====admin_config_validation=====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object. This function can be called statically or non statically.&lt;br /&gt;
&lt;br /&gt;
=====user_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;
=====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_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 an array of config options, the keys being nice strings to display to the user and the values being the selected config values.&lt;br /&gt;
&lt;br /&gt;
=====steal_control=====&lt;br /&gt;
&lt;br /&gt;
During any part of the export process, a plugin can completely steal control away from portfolio/add.php. This is useful, for example, for plugins that need the user go to log into a remote system and grant an application access.  It could be also used for a completely custom screen provided by the plugin.  If you need this, override this function to return a url, and the user will be redirected there.  When you&#039;re finished, return to $CFG-&amp;gt;wwwroot/portfolio/add.php?postcontrol=1 and processing will continue.   If you override this, it might be useful to also override post_control.&lt;br /&gt;
&lt;br /&gt;
=====post_control=====&lt;br /&gt;
&lt;br /&gt;
After control is returned after steal_control, post_control will be called before the next stage is processed, and passed any request parameters.  For an example of how this is used, see the box.net plugin, which uses steal_control to redirect to box.net to get the user to authenticate and then box.net redirects back to a url passing an authentication token to use for the rest of the session.  Since it&#039;s part of the request parameters, it&#039;s passed through to post_control, which stores whatever it needs before the next stage.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_user_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of user entered configuration data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
=====get_allowed_export_config=====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of per-export config data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed (note that if you store non-user entered config data you must implement this function)&lt;br /&gt;
&lt;br /&gt;
=====instance_sanity_check=====&lt;br /&gt;
&lt;br /&gt;
Similar to plugin_sanity_check although operates on an instance basis.   Again, returning anything non empty here will be taken as an error string, and the instance set to invisible.&lt;br /&gt;
&lt;br /&gt;
=====send_file (pull only)=====&lt;br /&gt;
&lt;br /&gt;
Sends the file to STDOUT (the browser in the case of the download plugin but may be an external system requesting it) - this function gets called when portfolio/file.php is requested and by default just passes the contents of the file straight through to the browser, unencrypted.  The other pull plugin this far is mahara, and it doesn&#039;t implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.&lt;br /&gt;
&lt;br /&gt;
=====cleanup=====&lt;br /&gt;
&lt;br /&gt;
if you&#039;ve stashed anything in extra database tables, you can implement this function to clean them up.  this is called on cron to cleanup expired transfers, as well as after a successful transfer.&lt;br /&gt;
&lt;br /&gt;
=====mnet_publishes=====&lt;br /&gt;
&lt;br /&gt;
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)&lt;br /&gt;
&lt;br /&gt;
=====get_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
In some cases, the transfer is logged from somewhere other than the interactive browser session (eg when a pull plugin completes the transfer before the browser is redirected to the finish page). The static_continue_url is the one that is finally logged and inserted into the log table, and used to display later to the user (in the Transfer log screen under portfolios in their profile).  This, combined with resolve_static_continue_url, are used to generate a new url later to the user.&lt;br /&gt;
&lt;br /&gt;
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid.&lt;br /&gt;
The static continue url just contains the &amp;quot;wantsurl&amp;quot; part of that in mahara - eg artefact/file/&lt;br /&gt;
resolve_static_continue_url converts the static url into a new mnet jump session url.&lt;br /&gt;
&lt;br /&gt;
=====resolve_static_continue_url=====&lt;br /&gt;
&lt;br /&gt;
See get_static_continue_url.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
===Methods you shouldn&#039;t really override===&lt;br /&gt;
&lt;br /&gt;
====Static functions====&lt;br /&gt;
&lt;br /&gt;
=====create_instance=====&lt;br /&gt;
&lt;br /&gt;
Creates an instance of the given plugin.  As well as plugin and name, this can also be passed initial admin config. Returns an object of the correct subclass.&lt;br /&gt;
&lt;br /&gt;
====Class methods====&lt;br /&gt;
&lt;br /&gt;
=====__construct=====&lt;br /&gt;
&lt;br /&gt;
Constructor for the plugin.  Subclasses don&#039;t need to override this.&lt;br /&gt;
&lt;br /&gt;
=====save=====&lt;br /&gt;
&lt;br /&gt;
Saves data stored in the object back to the database and resets the dirty flag.&lt;br /&gt;
&lt;br /&gt;
=====delete=====&lt;br /&gt;
&lt;br /&gt;
Deletes this instance and all configuration data associated with it completely from the database&lt;br /&gt;
&lt;br /&gt;
=====set_export_config=====&lt;br /&gt;
&lt;br /&gt;
I had originally made this function final in the base class, but the box.net plugin needs to override it for dependent export config that&#039;s a bit funky.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Database tables==&lt;br /&gt;
[[Category:Portfolios]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31803</id>
		<title>Portfolio plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31803"/>
		<updated>2012-01-25T10:02:13Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).&lt;br /&gt;
&lt;br /&gt;
For the purposes of this tutorial, it will be assumed you want to create a plugin called &amp;quot;foo&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Create a new directory in portfolio/ with the name of your plugin, for example, portfolio/foo.&lt;br /&gt;
&lt;br /&gt;
You need to create the standard [[version.php]] in here that you would normally create for most other plugin types in Moodle.&lt;br /&gt;
&lt;br /&gt;
You can also provide a db/ directory with an install.xml and upgrade.php along the same lines.  These are optional.&lt;br /&gt;
&lt;br /&gt;
Create a lib.php inside this directory.&lt;br /&gt;
&lt;br /&gt;
Now we&#039;re going to create the necessary subclass to make our plugin work.  It must be called portfolio_plugin_foo and directly extend either portfolio_plugin_push_base, or portfolio_plugin_pull_base.   The only real differences between them are that one pushes the package directly to the remote system (usually via a HTTP POST, but could be filesystem based), and the other type (pull) requires the remote system to request it.  portfolio_plugin_pull_base adds just one extra further abstract function (see below)&lt;br /&gt;
&lt;br /&gt;
Read the phpdoc documentation for the base classes for further information about each function, including the arguments it takes and the expected return type.  The following documentation serves as an overview.&lt;br /&gt;
&lt;br /&gt;
===Error handling===&lt;br /&gt;
Your code should always throw exceptions of type portfolio_plugin_exception.  You should NOT call error or print_error or whatever, and also any third party exceptions should really be caught and rethrown although most of the time other exceptions will be caught too by the portfolio code (although this will trigger a warning in debug mode)&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
&lt;br /&gt;
If your plugin isn&#039;t going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/. &lt;br /&gt;
Help files behave a little differently, you need to make portfolio/type/foo/lang/$language/help/foo/helpfile.html (this surprised me).&lt;br /&gt;
&lt;br /&gt;
Both normal strings and helpfiles should be called using module portfolio_foo for non core plugins. For core plugins, normal strings should be called using portfolio_foo but helpstrings should be called using portfolio/&lt;br /&gt;
&lt;br /&gt;
===File access===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;All&#039;&#039;&#039; of the handling of files using the Files API should go through the portfolio exporter object.  There are various helper methods you can use (see prepare_package documentation) in the exporter object.  The reason for this is that this object is mocked during unit tests so that files don&#039;t actually get moved around while tests are running.&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
&lt;br /&gt;
==Template==&lt;br /&gt;
Please refer to portfolio goodledocs plugins ([http://git.moodle.org/gw?p=moodle.git;a=tree;f=portfolio/googledocs;h=6d5e67e99545dc7430df5ad065d4d5da8945417d;hb=master portfolio/googledocs/]) as template to start with.&lt;br /&gt;
&lt;br /&gt;
==Naming convention==&lt;br /&gt;
&lt;br /&gt;
Plugin class name should start with &amp;lt;tt&amp;gt;portfolio_plugin_xxx&amp;lt;/tt&amp;gt;, where xxx refer to the name of the plugin. This class should also extends from &amp;lt;tt&amp;gt;portfolio_plugin_push_base&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;portfolio_plugin_pull_base&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
class portfolio_plugin_foo extends portfolio_plugin_push_base {}&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
For more information regarding naming convention, please refer to: [[Frankenstyle|Frankenstyle page]]&lt;br /&gt;
&lt;br /&gt;
==Methods you must override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
====get_name====&lt;br /&gt;
&lt;br /&gt;
Return a localized name for this plugin. Usually just something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;pluginname&#039;, &#039;portfolio_yourplugin&#039;); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is enforced as an abstract function rather than a loose fuzzy dependence on a language string for maximum ease when developing a new plugin (it&#039;s blindly obvious you must implement it ;) )&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====prepare_package====&lt;br /&gt;
&lt;br /&gt;
Does anything necessary to prepare the package for sending.  This might be writing out a metadata manifest file, or zipping up all the files in the temporary directory.  This is called after the corresponding prepare_package method in the caller, which writes files out to a temporary location.  Files can then be retrieved from there using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles, which returns an array of stored_file objects.  You can zip files using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;zip_tempfiles.  You &#039;&#039;&#039;must&#039;&#039;&#039; not use the files api directly.&lt;br /&gt;
&lt;br /&gt;
====send_package====&lt;br /&gt;
&lt;br /&gt;
Actually send the package to the remote system (this might be just sending an xmlrpc request to say a file is ready for the remote system to fetch, (in which case you must set the $file member variable for portfolio/file.php later, or actually transfering the file). You can retrieve the files the caller has written out using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles() which returns an array of stored_file objects.&lt;br /&gt;
&lt;br /&gt;
====get_interactive_continue_url====&lt;br /&gt;
&lt;br /&gt;
Return a url to present to the user as a &#039;continue to their portfolio&#039; link.  This can return false, although then you might want to implement get_extra_finish_options.   See also get_static_continue_url and resolve_static_continue_url&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====verify_file_request_params (pull only)====&lt;br /&gt;
&lt;br /&gt;
When remote systems request files, access control check is delegated to the plugin, by passing the array of request parameters. Should this function return false, access to the file will be denied.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====expected_time====&lt;br /&gt;
&lt;br /&gt;
How long a transfer can reasonably expect to take.  This function is passed the estimate from the caller (which usually takes into account filesize) and can either agree with it by returning whatever it is given, or override it.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH. 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;
&lt;br /&gt;
==Methods you can override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====supported_formats====&lt;br /&gt;
&lt;br /&gt;
The formats this plugin 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 parent class defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
====plugin_sanity_check====&lt;br /&gt;
&lt;br /&gt;
If the plugin depends on some other part of Moodle being configured in a particular way (for example if your plugin relies on using mnet for transport and mnet is off), you can override this function.  It is called frequently in different places in the code, and whenever a non empty value is returned (an error code), all instances of your plugin will be set to invisible.&lt;br /&gt;
&lt;br /&gt;
====has_admin_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin has some admininistrative configuration settings (rather than by the user), override this plugin to return true. You must also override admin_config_form and get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====get_allowed_config====&lt;br /&gt;
&lt;br /&gt;
Because most of the handling of the getting and setting of admin entered configuration data happens in the parent class, with no need to be overridden in the subclasses, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
====allows_multiple_instances====&lt;br /&gt;
&lt;br /&gt;
By default, all plugins can have multiple instances configured.  If it&#039;s only sensible for your plugin to be able to have one instance configured, override this to return false.&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====has_user_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin can be configured in the user profile section, override this function to return true. You must also override user_config_form and get_allowed_user_config.  You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
====has_export_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin can have further (user) configuration during export, override this function to return true. You must also override export_config_form, get_export_summary and get_allowed_export_config.  You can additionally override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
====admin_config_form====&lt;br /&gt;
&lt;br /&gt;
This function can actually be called statically or non statically, depending on whether it&#039;s for editing an existing instance of the plugin, or for creating a new one.  It&#039;s passed an mform object by reference, as are the other two config form functions, to add elements to it.  If you override this you don&#039;t need to handle setting the data of the elements (when editing), that&#039;s done in the caller, using get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
====user_config_form====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_user_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.   You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
====export_config_form====&lt;br /&gt;
    &lt;br /&gt;
If your plugin has overridden has_export_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.  You can also override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
====admin_config_validation====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object. This function can be called statically or non statically.&lt;br /&gt;
&lt;br /&gt;
====user_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;
====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_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 an array of config options, the keys being nice strings to display to the user and the values being the selected config values.&lt;br /&gt;
&lt;br /&gt;
====steal_control====&lt;br /&gt;
&lt;br /&gt;
During any part of the export process, a plugin can completely steal control away from portfolio/add.php. This is useful, for example, for plugins that need the user go to log into a remote system and grant an application access.  It could be also used for a completely custom screen provided by the plugin.  If you need this, override this function to return a url, and the user will be redirected there.  When you&#039;re finished, return to $CFG-&amp;gt;wwwroot/portfolio/add.php?postcontrol=1 and processing will continue.   If you override this, it might be useful to also override post_control.&lt;br /&gt;
&lt;br /&gt;
====post_control====&lt;br /&gt;
&lt;br /&gt;
After control is returned after steal_control, post_control will be called before the next stage is processed, and passed any request parameters.  For an example of how this is used, see the box.net plugin, which uses steal_control to redirect to box.net to get the user to authenticate and then box.net redirects back to a url passing an authentication token to use for the rest of the session.  Since it&#039;s part of the request parameters, it&#039;s passed through to post_control, which stores whatever it needs before the next stage.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_user_config====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of user entered configuration data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_export_config====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of per-export config data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed (note that if you store non-user entered config data you must implement this function)&lt;br /&gt;
&lt;br /&gt;
====instance_sanity_check====&lt;br /&gt;
&lt;br /&gt;
Similar to plugin_sanity_check although operates on an instance basis.   Again, returning anything non empty here will be taken as an error string, and the instance set to invisible.&lt;br /&gt;
&lt;br /&gt;
====send_file (pull only)====&lt;br /&gt;
&lt;br /&gt;
Sends the file to STDOUT (the browser in the case of the download plugin but may be an external system requesting it) - this function gets called when portfolio/file.php is requested and by default just passes the contents of the file straight through to the browser, unencrypted.  The other pull plugin this far is mahara, and it doesn&#039;t implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.&lt;br /&gt;
&lt;br /&gt;
====cleanup====&lt;br /&gt;
&lt;br /&gt;
if you&#039;ve stashed anything in extra database tables, you can implement this function to clean them up.  this is called on cron to cleanup expired transfers, as well as after a successful transfer.&lt;br /&gt;
&lt;br /&gt;
====mnet_publishes====&lt;br /&gt;
&lt;br /&gt;
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)&lt;br /&gt;
&lt;br /&gt;
====get_static_continue_url====&lt;br /&gt;
&lt;br /&gt;
In some cases, the transfer is logged from somewhere other than the interactive browser session (eg when a pull plugin completes the transfer before the browser is redirected to the finish page). The static_continue_url is the one that is finally logged and inserted into the log table, and used to display later to the user (in the Transfer log screen under portfolios in their profile).  This, combined with resolve_static_continue_url, are used to generate a new url later to the user.&lt;br /&gt;
&lt;br /&gt;
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid.&lt;br /&gt;
The static continue url just contains the &amp;quot;wantsurl&amp;quot; part of that in mahara - eg artefact/file/&lt;br /&gt;
resolve_static_continue_url converts the static url into a new mnet jump session url.&lt;br /&gt;
&lt;br /&gt;
====resolve_static_continue_url====&lt;br /&gt;
&lt;br /&gt;
See get_static_continue_url.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Methods you shouldn&#039;t really override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
====create_instance====&lt;br /&gt;
&lt;br /&gt;
Creates an instance of the given plugin.  As well as plugin and name, this can also be passed initial admin config. Returns an object of the correct subclass.&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====__construct====&lt;br /&gt;
&lt;br /&gt;
Constructor for the plugin.  Subclasses don&#039;t need to override this.&lt;br /&gt;
&lt;br /&gt;
====save====&lt;br /&gt;
&lt;br /&gt;
Saves data stored in the object back to the database and resets the dirty flag.&lt;br /&gt;
&lt;br /&gt;
====delete====&lt;br /&gt;
&lt;br /&gt;
Deletes this instance and all configuration data associated with it completely from the database&lt;br /&gt;
&lt;br /&gt;
====set_export_config====&lt;br /&gt;
&lt;br /&gt;
I had originally made this function final in the base class, but the box.net plugin needs to override it for dependent export config that&#039;s a bit funky.&lt;br /&gt;
[[Category:Portfolios]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31786</id>
		<title>Portfolio plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31786"/>
		<updated>2012-01-25T09:20:20Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: /* Example */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).&lt;br /&gt;
&lt;br /&gt;
For the purposes of this tutorial, it will be assumed you want to create a plugin called &amp;quot;foo&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Create a new directory in portfolio/ with the name of your plugin, for example, portfolio/foo.&lt;br /&gt;
&lt;br /&gt;
You need to create the standard [[version.php]] in here that you would normally create for most other plugin types in Moodle.&lt;br /&gt;
&lt;br /&gt;
You can also provide a db/ directory with an install.xml and upgrade.php along the same lines.  These are optional.&lt;br /&gt;
&lt;br /&gt;
Create a lib.php inside this directory.&lt;br /&gt;
&lt;br /&gt;
Now we&#039;re going to create the necessary subclass to make our plugin work.  It must be called portfolio_plugin_foo and directly extend either portfolio_plugin_push_base, or portfolio_plugin_pull_base.   The only real differences between them are that one pushes the package directly to the remote system (usually via a HTTP POST, but could be filesystem based), and the other type (pull) requires the remote system to request it.  portfolio_plugin_pull_base adds just one extra further abstract function (see below)&lt;br /&gt;
&lt;br /&gt;
Read the phpdoc documentation for the base classes for further information about each function, including the arguments it takes and the expected return type.  The following documentation serves as an overview.&lt;br /&gt;
&lt;br /&gt;
===Error handling===&lt;br /&gt;
Your code should always throw exceptions of type portfolio_plugin_exception.  You should NOT call error or print_error or whatever, and also any third party exceptions should really be caught and rethrown although most of the time other exceptions will be caught too by the portfolio code (although this will trigger a warning in debug mode)&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
&lt;br /&gt;
If your plugin isn&#039;t going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/. &lt;br /&gt;
Help files behave a little differently, you need to make portfolio/type/foo/lang/$language/help/foo/helpfile.html (this surprised me).&lt;br /&gt;
&lt;br /&gt;
Both normal strings and helpfiles should be called using module portfolio_foo for non core plugins. For core plugins, normal strings should be called using portfolio_foo but helpstrings should be called using portfolio/&lt;br /&gt;
&lt;br /&gt;
===File access===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;All&#039;&#039;&#039; of the handling of files using the Files API should go through the portfolio exporter object.  There are various helper methods you can use (see prepare_package documentation) in the exporter object.  The reason for this is that this object is mocked during unit tests so that files don&#039;t actually get moved around while tests are running.&lt;br /&gt;
&lt;br /&gt;
==Methods you must override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
====get_name====&lt;br /&gt;
&lt;br /&gt;
Return a localized name for this plugin. Usually just something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;pluginname&#039;, &#039;portfolio_yourplugin&#039;); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is enforced as an abstract function rather than a loose fuzzy dependence on a language string for maximum ease when developing a new plugin (it&#039;s blindly obvious you must implement it ;) )&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====prepare_package====&lt;br /&gt;
&lt;br /&gt;
Does anything necessary to prepare the package for sending.  This might be writing out a metadata manifest file, or zipping up all the files in the temporary directory.  This is called after the corresponding prepare_package method in the caller, which writes files out to a temporary location.  Files can then be retrieved from there using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles, which returns an array of stored_file objects.  You can zip files using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;zip_tempfiles.  You &#039;&#039;&#039;must&#039;&#039;&#039; not use the files api directly.&lt;br /&gt;
&lt;br /&gt;
====send_package====&lt;br /&gt;
&lt;br /&gt;
Actually send the package to the remote system (this might be just sending an xmlrpc request to say a file is ready for the remote system to fetch, (in which case you must set the $file member variable for portfolio/file.php later, or actually transfering the file). You can retrieve the files the caller has written out using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles() which returns an array of stored_file objects.&lt;br /&gt;
&lt;br /&gt;
====get_interactive_continue_url====&lt;br /&gt;
&lt;br /&gt;
Return a url to present to the user as a &#039;continue to their portfolio&#039; link.  This can return false, although then you might want to implement get_extra_finish_options.   See also get_static_continue_url and resolve_static_continue_url&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====verify_file_request_params (pull only)====&lt;br /&gt;
&lt;br /&gt;
When remote systems request files, access control check is delegated to the plugin, by passing the array of request parameters. Should this function return false, access to the file will be denied.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====expected_time====&lt;br /&gt;
&lt;br /&gt;
How long a transfer can reasonably expect to take.  This function is passed the estimate from the caller (which usually takes into account filesize) and can either agree with it by returning whatever it is given, or override it.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH. 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;
&lt;br /&gt;
==Methods you can override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====supported_formats====&lt;br /&gt;
&lt;br /&gt;
The formats this plugin 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 parent class defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
====plugin_sanity_check====&lt;br /&gt;
&lt;br /&gt;
If the plugin depends on some other part of Moodle being configured in a particular way (for example if your plugin relies on using mnet for transport and mnet is off), you can override this function.  It is called frequently in different places in the code, and whenever a non empty value is returned (an error code), all instances of your plugin will be set to invisible.&lt;br /&gt;
&lt;br /&gt;
====has_admin_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin has some admininistrative configuration settings (rather than by the user), override this plugin to return true. You must also override admin_config_form and get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====get_allowed_config====&lt;br /&gt;
&lt;br /&gt;
Because most of the handling of the getting and setting of admin entered configuration data happens in the parent class, with no need to be overridden in the subclasses, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
====allows_multiple_instances====&lt;br /&gt;
&lt;br /&gt;
By default, all plugins can have multiple instances configured.  If it&#039;s only sensible for your plugin to be able to have one instance configured, override this to return false.&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====has_user_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin can be configured in the user profile section, override this function to return true. You must also override user_config_form and get_allowed_user_config.  You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
====has_export_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin can have further (user) configuration during export, override this function to return true. You must also override export_config_form, get_export_summary and get_allowed_export_config.  You can additionally override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
====admin_config_form====&lt;br /&gt;
&lt;br /&gt;
This function can actually be called statically or non statically, depending on whether it&#039;s for editing an existing instance of the plugin, or for creating a new one.  It&#039;s passed an mform object by reference, as are the other two config form functions, to add elements to it.  If you override this you don&#039;t need to handle setting the data of the elements (when editing), that&#039;s done in the caller, using get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
====user_config_form====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_user_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.   You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
====export_config_form====&lt;br /&gt;
    &lt;br /&gt;
If your plugin has overridden has_export_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.  You can also override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
====admin_config_validation====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object. This function can be called statically or non statically.&lt;br /&gt;
&lt;br /&gt;
====user_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;
====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_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 an array of config options, the keys being nice strings to display to the user and the values being the selected config values.&lt;br /&gt;
&lt;br /&gt;
====steal_control====&lt;br /&gt;
&lt;br /&gt;
During any part of the export process, a plugin can completely steal control away from portfolio/add.php. This is useful, for example, for plugins that need the user go to log into a remote system and grant an application access.  It could be also used for a completely custom screen provided by the plugin.  If you need this, override this function to return a url, and the user will be redirected there.  When you&#039;re finished, return to $CFG-&amp;gt;wwwroot/portfolio/add.php?postcontrol=1 and processing will continue.   If you override this, it might be useful to also override post_control.&lt;br /&gt;
&lt;br /&gt;
====post_control====&lt;br /&gt;
&lt;br /&gt;
After control is returned after steal_control, post_control will be called before the next stage is processed, and passed any request parameters.  For an example of how this is used, see the box.net plugin, which uses steal_control to redirect to box.net to get the user to authenticate and then box.net redirects back to a url passing an authentication token to use for the rest of the session.  Since it&#039;s part of the request parameters, it&#039;s passed through to post_control, which stores whatever it needs before the next stage.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_user_config====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of user entered configuration data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_export_config====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of per-export config data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed (note that if you store non-user entered config data you must implement this function)&lt;br /&gt;
&lt;br /&gt;
====instance_sanity_check====&lt;br /&gt;
&lt;br /&gt;
Similar to plugin_sanity_check although operates on an instance basis.   Again, returning anything non empty here will be taken as an error string, and the instance set to invisible.&lt;br /&gt;
&lt;br /&gt;
====send_file (pull only)====&lt;br /&gt;
&lt;br /&gt;
Sends the file to STDOUT (the browser in the case of the download plugin but may be an external system requesting it) - this function gets called when portfolio/file.php is requested and by default just passes the contents of the file straight through to the browser, unencrypted.  The other pull plugin this far is mahara, and it doesn&#039;t implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.&lt;br /&gt;
&lt;br /&gt;
====cleanup====&lt;br /&gt;
&lt;br /&gt;
if you&#039;ve stashed anything in extra database tables, you can implement this function to clean them up.  this is called on cron to cleanup expired transfers, as well as after a successful transfer.&lt;br /&gt;
&lt;br /&gt;
====mnet_publishes====&lt;br /&gt;
&lt;br /&gt;
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)&lt;br /&gt;
&lt;br /&gt;
====get_static_continue_url====&lt;br /&gt;
&lt;br /&gt;
In some cases, the transfer is logged from somewhere other than the interactive browser session (eg when a pull plugin completes the transfer before the browser is redirected to the finish page). The static_continue_url is the one that is finally logged and inserted into the log table, and used to display later to the user (in the Transfer log screen under portfolios in their profile).  This, combined with resolve_static_continue_url, are used to generate a new url later to the user.&lt;br /&gt;
&lt;br /&gt;
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid.&lt;br /&gt;
The static continue url just contains the &amp;quot;wantsurl&amp;quot; part of that in mahara - eg artefact/file/&lt;br /&gt;
resolve_static_continue_url converts the static url into a new mnet jump session url.&lt;br /&gt;
&lt;br /&gt;
====resolve_static_continue_url====&lt;br /&gt;
&lt;br /&gt;
See get_static_continue_url.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Methods you shouldn&#039;t really override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
====create_instance====&lt;br /&gt;
&lt;br /&gt;
Creates an instance of the given plugin.  As well as plugin and name, this can also be passed initial admin config. Returns an object of the correct subclass.&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====__construct====&lt;br /&gt;
&lt;br /&gt;
Constructor for the plugin.  Subclasses don&#039;t need to override this.&lt;br /&gt;
&lt;br /&gt;
====save====&lt;br /&gt;
&lt;br /&gt;
Saves data stored in the object back to the database and resets the dirty flag.&lt;br /&gt;
&lt;br /&gt;
====delete====&lt;br /&gt;
&lt;br /&gt;
Deletes this instance and all configuration data associated with it completely from the database&lt;br /&gt;
&lt;br /&gt;
====set_export_config====&lt;br /&gt;
&lt;br /&gt;
I had originally made this function final in the base class, but the box.net plugin needs to override it for dependent export config that&#039;s a bit funky.&lt;br /&gt;
[[Category:Portfolios]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31785</id>
		<title>Portfolio plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31785"/>
		<updated>2012-01-25T09:19:02Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: /* Example */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
For the purposes of this tutorial, it will be assumed you want to create a plugin called &amp;quot;foo&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Create a new directory in portfolio/ with the name of your plugin, for example, portfolio/foo.&lt;br /&gt;
&lt;br /&gt;
You need to create the standard [[version.php]] in here that you would normally create for most other plugin types in Moodle.&lt;br /&gt;
&lt;br /&gt;
You can also provide a db/ directory with an install.xml and upgrade.php along the same lines.  These are optional.&lt;br /&gt;
&lt;br /&gt;
Create a lib.php inside this directory.&lt;br /&gt;
&lt;br /&gt;
Now we&#039;re going to create the necessary subclass to make our plugin work.  It must be called portfolio_plugin_foo and directly extend either portfolio_plugin_push_base, or portfolio_plugin_pull_base.   The only real differences between them are that one pushes the package directly to the remote system (usually via a HTTP POST, but could be filesystem based), and the other type (pull) requires the remote system to request it.  portfolio_plugin_pull_base adds just one extra further abstract function (see below)&lt;br /&gt;
&lt;br /&gt;
Read the phpdoc documentation for the base classes for further information about each function, including the arguments it takes and the expected return type.  The following documentation serves as an overview.&lt;br /&gt;
&lt;br /&gt;
===Error handling===&lt;br /&gt;
Your code should always throw exceptions of type portfolio_plugin_exception.  You should NOT call error or print_error or whatever, and also any third party exceptions should really be caught and rethrown although most of the time other exceptions will be caught too by the portfolio code (although this will trigger a warning in debug mode)&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
&lt;br /&gt;
If your plugin isn&#039;t going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/. &lt;br /&gt;
Help files behave a little differently, you need to make portfolio/type/foo/lang/$language/help/foo/helpfile.html (this surprised me).&lt;br /&gt;
&lt;br /&gt;
Both normal strings and helpfiles should be called using module portfolio_foo for non core plugins. For core plugins, normal strings should be called using portfolio_foo but helpstrings should be called using portfolio/&lt;br /&gt;
&lt;br /&gt;
===File access===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;All&#039;&#039;&#039; of the handling of files using the Files API should go through the portfolio exporter object.  There are various helper methods you can use (see prepare_package documentation) in the exporter object.  The reason for this is that this object is mocked during unit tests so that files don&#039;t actually get moved around while tests are running.&lt;br /&gt;
&lt;br /&gt;
==Methods you must override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
====get_name====&lt;br /&gt;
&lt;br /&gt;
Return a localized name for this plugin. Usually just something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;pluginname&#039;, &#039;portfolio_yourplugin&#039;); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is enforced as an abstract function rather than a loose fuzzy dependence on a language string for maximum ease when developing a new plugin (it&#039;s blindly obvious you must implement it ;) )&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====prepare_package====&lt;br /&gt;
&lt;br /&gt;
Does anything necessary to prepare the package for sending.  This might be writing out a metadata manifest file, or zipping up all the files in the temporary directory.  This is called after the corresponding prepare_package method in the caller, which writes files out to a temporary location.  Files can then be retrieved from there using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles, which returns an array of stored_file objects.  You can zip files using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;zip_tempfiles.  You &#039;&#039;&#039;must&#039;&#039;&#039; not use the files api directly.&lt;br /&gt;
&lt;br /&gt;
====send_package====&lt;br /&gt;
&lt;br /&gt;
Actually send the package to the remote system (this might be just sending an xmlrpc request to say a file is ready for the remote system to fetch, (in which case you must set the $file member variable for portfolio/file.php later, or actually transfering the file). You can retrieve the files the caller has written out using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles() which returns an array of stored_file objects.&lt;br /&gt;
&lt;br /&gt;
====get_interactive_continue_url====&lt;br /&gt;
&lt;br /&gt;
Return a url to present to the user as a &#039;continue to their portfolio&#039; link.  This can return false, although then you might want to implement get_extra_finish_options.   See also get_static_continue_url and resolve_static_continue_url&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====verify_file_request_params (pull only)====&lt;br /&gt;
&lt;br /&gt;
When remote systems request files, access control check is delegated to the plugin, by passing the array of request parameters. Should this function return false, access to the file will be denied.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====expected_time====&lt;br /&gt;
&lt;br /&gt;
How long a transfer can reasonably expect to take.  This function is passed the estimate from the caller (which usually takes into account filesize) and can either agree with it by returning whatever it is given, or override it.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH. 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;
&lt;br /&gt;
==Methods you can override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====supported_formats====&lt;br /&gt;
&lt;br /&gt;
The formats this plugin 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 parent class defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
====plugin_sanity_check====&lt;br /&gt;
&lt;br /&gt;
If the plugin depends on some other part of Moodle being configured in a particular way (for example if your plugin relies on using mnet for transport and mnet is off), you can override this function.  It is called frequently in different places in the code, and whenever a non empty value is returned (an error code), all instances of your plugin will be set to invisible.&lt;br /&gt;
&lt;br /&gt;
====has_admin_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin has some admininistrative configuration settings (rather than by the user), override this plugin to return true. You must also override admin_config_form and get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====get_allowed_config====&lt;br /&gt;
&lt;br /&gt;
Because most of the handling of the getting and setting of admin entered configuration data happens in the parent class, with no need to be overridden in the subclasses, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
====allows_multiple_instances====&lt;br /&gt;
&lt;br /&gt;
By default, all plugins can have multiple instances configured.  If it&#039;s only sensible for your plugin to be able to have one instance configured, override this to return false.&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====has_user_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin can be configured in the user profile section, override this function to return true. You must also override user_config_form and get_allowed_user_config.  You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
====has_export_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin can have further (user) configuration during export, override this function to return true. You must also override export_config_form, get_export_summary and get_allowed_export_config.  You can additionally override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
====admin_config_form====&lt;br /&gt;
&lt;br /&gt;
This function can actually be called statically or non statically, depending on whether it&#039;s for editing an existing instance of the plugin, or for creating a new one.  It&#039;s passed an mform object by reference, as are the other two config form functions, to add elements to it.  If you override this you don&#039;t need to handle setting the data of the elements (when editing), that&#039;s done in the caller, using get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
====user_config_form====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_user_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.   You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
====export_config_form====&lt;br /&gt;
    &lt;br /&gt;
If your plugin has overridden has_export_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.  You can also override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
====admin_config_validation====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object. This function can be called statically or non statically.&lt;br /&gt;
&lt;br /&gt;
====user_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;
====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_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 an array of config options, the keys being nice strings to display to the user and the values being the selected config values.&lt;br /&gt;
&lt;br /&gt;
====steal_control====&lt;br /&gt;
&lt;br /&gt;
During any part of the export process, a plugin can completely steal control away from portfolio/add.php. This is useful, for example, for plugins that need the user go to log into a remote system and grant an application access.  It could be also used for a completely custom screen provided by the plugin.  If you need this, override this function to return a url, and the user will be redirected there.  When you&#039;re finished, return to $CFG-&amp;gt;wwwroot/portfolio/add.php?postcontrol=1 and processing will continue.   If you override this, it might be useful to also override post_control.&lt;br /&gt;
&lt;br /&gt;
====post_control====&lt;br /&gt;
&lt;br /&gt;
After control is returned after steal_control, post_control will be called before the next stage is processed, and passed any request parameters.  For an example of how this is used, see the box.net plugin, which uses steal_control to redirect to box.net to get the user to authenticate and then box.net redirects back to a url passing an authentication token to use for the rest of the session.  Since it&#039;s part of the request parameters, it&#039;s passed through to post_control, which stores whatever it needs before the next stage.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_user_config====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of user entered configuration data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_export_config====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of per-export config data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed (note that if you store non-user entered config data you must implement this function)&lt;br /&gt;
&lt;br /&gt;
====instance_sanity_check====&lt;br /&gt;
&lt;br /&gt;
Similar to plugin_sanity_check although operates on an instance basis.   Again, returning anything non empty here will be taken as an error string, and the instance set to invisible.&lt;br /&gt;
&lt;br /&gt;
====send_file (pull only)====&lt;br /&gt;
&lt;br /&gt;
Sends the file to STDOUT (the browser in the case of the download plugin but may be an external system requesting it) - this function gets called when portfolio/file.php is requested and by default just passes the contents of the file straight through to the browser, unencrypted.  The other pull plugin this far is mahara, and it doesn&#039;t implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.&lt;br /&gt;
&lt;br /&gt;
====cleanup====&lt;br /&gt;
&lt;br /&gt;
if you&#039;ve stashed anything in extra database tables, you can implement this function to clean them up.  this is called on cron to cleanup expired transfers, as well as after a successful transfer.&lt;br /&gt;
&lt;br /&gt;
====mnet_publishes====&lt;br /&gt;
&lt;br /&gt;
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)&lt;br /&gt;
&lt;br /&gt;
====get_static_continue_url====&lt;br /&gt;
&lt;br /&gt;
In some cases, the transfer is logged from somewhere other than the interactive browser session (eg when a pull plugin completes the transfer before the browser is redirected to the finish page). The static_continue_url is the one that is finally logged and inserted into the log table, and used to display later to the user (in the Transfer log screen under portfolios in their profile).  This, combined with resolve_static_continue_url, are used to generate a new url later to the user.&lt;br /&gt;
&lt;br /&gt;
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid.&lt;br /&gt;
The static continue url just contains the &amp;quot;wantsurl&amp;quot; part of that in mahara - eg artefact/file/&lt;br /&gt;
resolve_static_continue_url converts the static url into a new mnet jump session url.&lt;br /&gt;
&lt;br /&gt;
====resolve_static_continue_url====&lt;br /&gt;
&lt;br /&gt;
See get_static_continue_url.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Methods you shouldn&#039;t really override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
====create_instance====&lt;br /&gt;
&lt;br /&gt;
Creates an instance of the given plugin.  As well as plugin and name, this can also be passed initial admin config. Returns an object of the correct subclass.&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====__construct====&lt;br /&gt;
&lt;br /&gt;
Constructor for the plugin.  Subclasses don&#039;t need to override this.&lt;br /&gt;
&lt;br /&gt;
====save====&lt;br /&gt;
&lt;br /&gt;
Saves data stored in the object back to the database and resets the dirty flag.&lt;br /&gt;
&lt;br /&gt;
====delete====&lt;br /&gt;
&lt;br /&gt;
Deletes this instance and all configuration data associated with it completely from the database&lt;br /&gt;
&lt;br /&gt;
====set_export_config====&lt;br /&gt;
&lt;br /&gt;
I had originally made this function final in the base class, but the box.net plugin needs to override it for dependent export config that&#039;s a bit funky.&lt;br /&gt;
[[Category:Portfolios]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31733</id>
		<title>Portfolio plugins</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Portfolio_plugins&amp;diff=31733"/>
		<updated>2012-01-25T07:30:16Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;==Introduction==&lt;br /&gt;
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).&lt;br /&gt;
&lt;br /&gt;
==Example==&lt;br /&gt;
For the purposes of this tutorial, it will be assumed you want to create a plugin called &amp;quot;foo&amp;quot;&lt;br /&gt;
&lt;br /&gt;
Create a new directory in portfolio/type/ with the name of your plugin, for example, portfolio/type/foo.&lt;br /&gt;
&lt;br /&gt;
You need to create the standard [[version.php]] in here that you would normally create for most other plugin types in Moodle.&lt;br /&gt;
&lt;br /&gt;
You can also provide a db/ directory with an install.xml and upgrade.php along the same lines.  These are optional.&lt;br /&gt;
&lt;br /&gt;
Create a lib.php inside this directory.&lt;br /&gt;
&lt;br /&gt;
Now we&#039;re going to create the necessary subclass to make our plugin work.  It must be called portfolio_plugin_foo and directly extend either portfolio_plugin_push_base, or portfolio_plugin_pull_base.   The only real differences between them are that one pushes the package directly to the remote system (usually via a HTTP POST, but could be filesystem based), and the other type (pull) requires the remote system to request it.  portfolio_plugin_pull_base adds just one extra further abstract function (see below)&lt;br /&gt;
&lt;br /&gt;
Read the phpdoc documentation for the base classes for further information about each function, including the arguments it takes and the expected return type.  The following documentation serves as an overview.&lt;br /&gt;
&lt;br /&gt;
===Error handling===&lt;br /&gt;
Your code should always throw exceptions of type portfolio_plugin_exception.  You should NOT call error or print_error or whatever, and also any third party exceptions should really be caught and rethrown although most of the time other exceptions will be caught too by the portfolio code (although this will trigger a warning in debug mode)&lt;br /&gt;
&lt;br /&gt;
===Language packs===&lt;br /&gt;
&lt;br /&gt;
If your plugin isn&#039;t going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/. &lt;br /&gt;
Help files behave a little differently, you need to make portfolio/type/foo/lang/$language/help/foo/helpfile.html (this surprised me).&lt;br /&gt;
&lt;br /&gt;
Both normal strings and helpfiles should be called using module portfolio_foo for non core plugins. For core plugins, normal strings should be called using portfolio_foo but helpstrings should be called using portfolio/&lt;br /&gt;
&lt;br /&gt;
===File access===&lt;br /&gt;
&lt;br /&gt;
&#039;&#039;&#039;All&#039;&#039;&#039; of the handling of files using the Files API should go through the portfolio exporter object.  There are various helper methods you can use (see prepare_package documentation) in the exporter object.  The reason for this is that this object is mocked during unit tests so that files don&#039;t actually get moved around while tests are running.&lt;br /&gt;
&lt;br /&gt;
==Methods you must override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
====get_name====&lt;br /&gt;
&lt;br /&gt;
Return a localized name for this plugin. Usually just something like:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
get_string(&#039;pluginname&#039;, &#039;portfolio_yourplugin&#039;); &lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This is enforced as an abstract function rather than a loose fuzzy dependence on a language string for maximum ease when developing a new plugin (it&#039;s blindly obvious you must implement it ;) )&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====prepare_package====&lt;br /&gt;
&lt;br /&gt;
Does anything necessary to prepare the package for sending.  This might be writing out a metadata manifest file, or zipping up all the files in the temporary directory.  This is called after the corresponding prepare_package method in the caller, which writes files out to a temporary location.  Files can then be retrieved from there using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles, which returns an array of stored_file objects.  You can zip files using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;zip_tempfiles.  You &#039;&#039;&#039;must&#039;&#039;&#039; not use the files api directly.&lt;br /&gt;
&lt;br /&gt;
====send_package====&lt;br /&gt;
&lt;br /&gt;
Actually send the package to the remote system (this might be just sending an xmlrpc request to say a file is ready for the remote system to fetch, (in which case you must set the $file member variable for portfolio/file.php later, or actually transfering the file). You can retrieve the files the caller has written out using $this-&amp;gt;get(&#039;exporter&#039;)-&amp;gt;get_tempfiles() which returns an array of stored_file objects.&lt;br /&gt;
&lt;br /&gt;
====get_interactive_continue_url====&lt;br /&gt;
&lt;br /&gt;
Return a url to present to the user as a &#039;continue to their portfolio&#039; link.  This can return false, although then you might want to implement get_extra_finish_options.   See also get_static_continue_url and resolve_static_continue_url&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====verify_file_request_params (pull only)====&lt;br /&gt;
&lt;br /&gt;
When remote systems request files, access control check is delegated to the plugin, by passing the array of request parameters. Should this function return false, access to the file will be denied.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====expected_time====&lt;br /&gt;
&lt;br /&gt;
How long a transfer can reasonably expect to take.  This function is passed the estimate from the caller (which usually takes into account filesize) and can either agree with it by returning whatever it is given, or override it.  There are three options, PORTFOLIO_TIME_LOW, PORTFOLIO_TIME_MODERATE, and PORTFOLIO_TIME_HIGH. 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;
&lt;br /&gt;
==Methods you can override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====supported_formats====&lt;br /&gt;
&lt;br /&gt;
The formats this plugin 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 parent class defines PORTFOLIO_FORMAT_FILE.&lt;br /&gt;
&lt;br /&gt;
====plugin_sanity_check====&lt;br /&gt;
&lt;br /&gt;
If the plugin depends on some other part of Moodle being configured in a particular way (for example if your plugin relies on using mnet for transport and mnet is off), you can override this function.  It is called frequently in different places in the code, and whenever a non empty value is returned (an error code), all instances of your plugin will be set to invisible.&lt;br /&gt;
&lt;br /&gt;
====has_admin_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin has some admininistrative configuration settings (rather than by the user), override this plugin to return true. You must also override admin_config_form and get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
====get_allowed_config====&lt;br /&gt;
&lt;br /&gt;
Because most of the handling of the getting and setting of admin entered configuration data happens in the parent class, with no need to be overridden in the subclasses, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
====allows_multiple_instances====&lt;br /&gt;
&lt;br /&gt;
By default, all plugins can have multiple instances configured.  If it&#039;s only sensible for your plugin to be able to have one instance configured, override this to return false.&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====has_user_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin can be configured in the user profile section, override this function to return true. You must also override user_config_form and get_allowed_user_config.  You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
====has_export_config====&lt;br /&gt;
&lt;br /&gt;
If your plugin can have further (user) configuration during export, override this function to return true. You must also override export_config_form, get_export_summary and get_allowed_export_config.  You can additionally override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
====admin_config_form====&lt;br /&gt;
&lt;br /&gt;
This function can actually be called statically or non statically, depending on whether it&#039;s for editing an existing instance of the plugin, or for creating a new one.  It&#039;s passed an mform object by reference, as are the other two config form functions, to add elements to it.  If you override this you don&#039;t need to handle setting the data of the elements (when editing), that&#039;s done in the caller, using get_allowed_config.  You can also override admin_config_validation.&lt;br /&gt;
&lt;br /&gt;
====user_config_form====&lt;br /&gt;
&lt;br /&gt;
If your plugin has overridden has_user_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.   You can also override user_config_validation.&lt;br /&gt;
&lt;br /&gt;
====export_config_form====&lt;br /&gt;
    &lt;br /&gt;
If your plugin has overridden has_export_config to return true, you must implement this function. It takes a moodleform object as a parameter (passed by reference) to have additional elements added to it by this function.  You can also override export_config_validation.&lt;br /&gt;
&lt;br /&gt;
====admin_config_validation====&lt;br /&gt;
&lt;br /&gt;
This follows the exact same format as the validation() function in the moodleform object. This function can be called statically or non statically.&lt;br /&gt;
&lt;br /&gt;
====user_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;
====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_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 an array of config options, the keys being nice strings to display to the user and the values being the selected config values.&lt;br /&gt;
&lt;br /&gt;
====steal_control====&lt;br /&gt;
&lt;br /&gt;
During any part of the export process, a plugin can completely steal control away from portfolio/add.php. This is useful, for example, for plugins that need the user go to log into a remote system and grant an application access.  It could be also used for a completely custom screen provided by the plugin.  If you need this, override this function to return a url, and the user will be redirected there.  When you&#039;re finished, return to $CFG-&amp;gt;wwwroot/portfolio/add.php?postcontrol=1 and processing will continue.   If you override this, it might be useful to also override post_control.&lt;br /&gt;
&lt;br /&gt;
====post_control====&lt;br /&gt;
&lt;br /&gt;
After control is returned after steal_control, post_control will be called before the next stage is processed, and passed any request parameters.  For an example of how this is used, see the box.net plugin, which uses steal_control to redirect to box.net to get the user to authenticate and then box.net redirects back to a url passing an authentication token to use for the rest of the session.  Since it&#039;s part of the request parameters, it&#039;s passed through to post_control, which stores whatever it needs before the next stage.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_user_config====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of user entered configuration data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed.&lt;br /&gt;
&lt;br /&gt;
====get_allowed_export_config====&lt;br /&gt;
&lt;br /&gt;
Because the setting and getting of per-export config data happens in the parent class, this method is responsible for letting the parent class know what fields are allowed (note that if you store non-user entered config data you must implement this function)&lt;br /&gt;
&lt;br /&gt;
====instance_sanity_check====&lt;br /&gt;
&lt;br /&gt;
Similar to plugin_sanity_check although operates on an instance basis.   Again, returning anything non empty here will be taken as an error string, and the instance set to invisible.&lt;br /&gt;
&lt;br /&gt;
====send_file (pull only)====&lt;br /&gt;
&lt;br /&gt;
Sends the file to STDOUT (the browser in the case of the download plugin but may be an external system requesting it) - this function gets called when portfolio/file.php is requested and by default just passes the contents of the file straight through to the browser, unencrypted.  The other pull plugin this far is mahara, and it doesn&#039;t implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.&lt;br /&gt;
&lt;br /&gt;
====cleanup====&lt;br /&gt;
&lt;br /&gt;
if you&#039;ve stashed anything in extra database tables, you can implement this function to clean them up.  this is called on cron to cleanup expired transfers, as well as after a successful transfer.&lt;br /&gt;
&lt;br /&gt;
====mnet_publishes====&lt;br /&gt;
&lt;br /&gt;
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)&lt;br /&gt;
&lt;br /&gt;
====get_static_continue_url====&lt;br /&gt;
&lt;br /&gt;
In some cases, the transfer is logged from somewhere other than the interactive browser session (eg when a pull plugin completes the transfer before the browser is redirected to the finish page). The static_continue_url is the one that is finally logged and inserted into the log table, and used to display later to the user (in the Transfer log screen under portfolios in their profile).  This, combined with resolve_static_continue_url, are used to generate a new url later to the user.&lt;br /&gt;
&lt;br /&gt;
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid.&lt;br /&gt;
The static continue url just contains the &amp;quot;wantsurl&amp;quot; part of that in mahara - eg artefact/file/&lt;br /&gt;
resolve_static_continue_url converts the static url into a new mnet jump session url.&lt;br /&gt;
&lt;br /&gt;
====resolve_static_continue_url====&lt;br /&gt;
&lt;br /&gt;
See get_static_continue_url.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==Methods you shouldn&#039;t really override==&lt;br /&gt;
&lt;br /&gt;
===Static functions===&lt;br /&gt;
&lt;br /&gt;
====create_instance====&lt;br /&gt;
&lt;br /&gt;
Creates an instance of the given plugin.  As well as plugin and name, this can also be passed initial admin config. Returns an object of the correct subclass.&lt;br /&gt;
&lt;br /&gt;
===Class methods===&lt;br /&gt;
&lt;br /&gt;
====__construct====&lt;br /&gt;
&lt;br /&gt;
Constructor for the plugin.  Subclasses don&#039;t need to override this.&lt;br /&gt;
&lt;br /&gt;
====save====&lt;br /&gt;
&lt;br /&gt;
Saves data stored in the object back to the database and resets the dirty flag.&lt;br /&gt;
&lt;br /&gt;
====delete====&lt;br /&gt;
&lt;br /&gt;
Deletes this instance and all configuration data associated with it completely from the database&lt;br /&gt;
&lt;br /&gt;
====set_export_config====&lt;br /&gt;
&lt;br /&gt;
I had originally made this function final in the base class, but the box.net plugin needs to override it for dependent export config that&#039;s a bit funky.&lt;br /&gt;
[[Category:Portfolios]]&lt;br /&gt;
&lt;br /&gt;
[[Category:Plugins]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Calendar_API&amp;diff=31516</id>
		<title>Calendar API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Calendar_API&amp;diff=31516"/>
		<updated>2012-01-17T06:35:42Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Calendar API allows you to add and modify events in the calendar for user, groups, courses, or the whole site.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The Moodle [[:en:Calendar|Calendar]] collects and displays calendar events from everything users have access to.&lt;br /&gt;
&lt;br /&gt;
If your plugin generates calendar events (such as due dates) then you need to add your events to the calendar.&lt;br /&gt;
&lt;br /&gt;
==File locations==&lt;br /&gt;
&lt;br /&gt;
All the calendar code is located in /calendar/lib.php.  You need to include this file in your script if you intend to use it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==The calendar_event class==&lt;br /&gt;
&lt;br /&gt;
In general functionality of calendar_event() class are to create, update and delete events.&lt;br /&gt;
&lt;br /&gt;
===Creating new event===&lt;br /&gt;
Creating new calendar event to database by defining some properties for the event.  &lt;br /&gt;
&lt;br /&gt;
If event hook is used, it will also call self::calendar_event_hook() to create event for the hook.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_event::create($properties)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating event===&lt;br /&gt;
Updating an existing event in database by providing at least an event id.  If the event is a repeated events, the rest of series event will also be updated (depending on the properties value of repeateditall).  This function could also be use to insert new event to database If the requested event is not exist in database.  The optional parameter $checkcapability is use to check user&#039;s capability to edit/add event.  By default $checkcapability parameter is set to true.  &lt;br /&gt;
&lt;br /&gt;
If event hook is used, it will also call self::calendar_event_hook() to update the hook event.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_event::update($data, $checkcapability = true)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting event===&lt;br /&gt;
Deleting an existing event in database.  The optional parameter $deleterepeated is use as indicator to remove the rest of repeated events.  The default value for $deleterepeated is true. Deleting an event will also deleting all associated files related to the event&#039;s editor context.&lt;br /&gt;
&lt;br /&gt;
If event hook is used, it will also call self::calendar_event_hook() to delete event for the hook.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_event::delete($deleterepeated = false)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Event hook===&lt;br /&gt;
The capability to use hook to perform specific action to calendar event.  This requires setting up $CFG-&amp;gt;calendar and include external calendar file in $CFG-&amp;gt;dirroot .&#039;/calendar/&#039;. $CFG-&amp;gt;calendar .&#039;/lib.php&#039;).&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_event_hook($action, array $args)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Functions==&lt;br /&gt;
List of function for calendar and calendar_event.&lt;br /&gt;
&lt;br /&gt;
===Retrieve or print calendar&#039;s information===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_get_default_courses()&lt;br /&gt;
calendar_get_days()&lt;br /&gt;
calendar_get_starting_weekday()&lt;br /&gt;
calendar_day_representation($tstamp, $now = false, $usecommonwords = true)&lt;br /&gt;
calendar_time_representation($time)&lt;br /&gt;
calendar_wday_name($englishname)&lt;br /&gt;
calendar_days_in_month($month, $year)&lt;br /&gt;
calendar_get_link_href($linkbase, $d, $m, $y)&lt;br /&gt;
calendar_get_mini($courses, $groups, $users, $cal_month = false, $cal_year = false)&lt;br /&gt;
calendar_get_popup($is_today, $event_timestart, $popupcontent = &#039;&#039;) &lt;br /&gt;
calendar_add_month($month, $year)&lt;br /&gt;
calendar_sub_month($month, $year)&lt;br /&gt;
calendar_get_module_cached(&amp;amp;$coursecache, $modulename, $instance) &lt;br /&gt;
calendar_get_course_cached(&amp;amp;$coursecache, $courseid)&lt;br /&gt;
calendar_print_month_selector($name, $selected)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Control calendar display===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_top_controls($type, $data)&lt;br /&gt;
calendar_filter_controls(moodle_url $returnurl)&lt;br /&gt;
calendar_preferences_button(stdClass $course)&lt;br /&gt;
calendar_set_filters(array $courseeventsfrom, $ignorefilters = false)&lt;br /&gt;
calendar_get_link_previous($text, $linkbase, $d, $m, $y, $accesshide = false)&lt;br /&gt;
calendar_get_link_next($text, $linkbase, $d, $m, $y, $accesshide = false)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Retrieve calendar_event information===&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_get_allowed_types(&amp;amp;$allowed, $course = null)&lt;br /&gt;
calendar_add_event_allowed($event) &lt;br /&gt;
calendar_edit_event_allowed($event)&lt;br /&gt;
calendar_user_can_add_event($course)&lt;br /&gt;
calendar_show_event_type($type, $user = null)&lt;br /&gt;
calendar_set_event_type_display($type, $display = null, $user = null)&lt;br /&gt;
calendar_get_events($tstart, $tend, $users, $groups, $courses, $withduration = true, $ignorehidden = true)&lt;br /&gt;
calendar_events_by_day($events, $month, $year, &amp;amp;$eventsbyday, &amp;amp;$durationbyday, &amp;amp;$typesbyday, &amp;amp;$courses) &lt;br /&gt;
calendar_get_upcoming($courses, $groups, $users, $daysinfuture, $maxevents, $fromtime = 0)&lt;br /&gt;
calendar_get_block_upcoming($events, $linkhref = NULL)&lt;br /&gt;
calendar_format_event_time($event, $now, $linkparams = null, $usecommonwords = true, $showtime = 0)&lt;br /&gt;
calendar_add_event_metadata($event)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Examples==&lt;br /&gt;
The following are examples of using the basic calendar_event API in Moodle.&lt;br /&gt;
&lt;br /&gt;
===Example to create new event===&lt;br /&gt;
Creating new calendar event for closing feedback date.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$event = new stdClass;&lt;br /&gt;
$event-&amp;gt;name         = get_string(&#039;stop&#039;, &#039;feedback&#039;).&#039; &#039;.$feedback-&amp;gt;name;&lt;br /&gt;
$event-&amp;gt;description  = format_module_intro(&#039;feedback&#039;, $feedback, $feedback-&amp;gt;coursemodule);&lt;br /&gt;
$event-&amp;gt;courseid     = $feedback-&amp;gt;course;&lt;br /&gt;
$event-&amp;gt;groupid      = 0;&lt;br /&gt;
$event-&amp;gt;userid       = 0;&lt;br /&gt;
$event-&amp;gt;modulename   = &#039;feedback&#039;;&lt;br /&gt;
$event-&amp;gt;instance     = $feedback-&amp;gt;id;&lt;br /&gt;
$event-&amp;gt;eventtype    = &#039;close&#039;;&lt;br /&gt;
$event-&amp;gt;timestart    = $feedback-&amp;gt;timeclose;&lt;br /&gt;
$event-&amp;gt;visible      = instance_is_visible(&#039;feedback&#039;, $feedback);&lt;br /&gt;
$event-&amp;gt;timeduration = 0;&lt;br /&gt;
&lt;br /&gt;
calendar_event::create($event);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating existing calendar event===&lt;br /&gt;
Simple example of updating exiting event through moodle form.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$eventid = required_param(&#039;id&#039;, PARAM_INT);&lt;br /&gt;
$event = calendar_event::load($eventid);&lt;br /&gt;
&lt;br /&gt;
$data = $mform-&amp;gt;get_data();&lt;br /&gt;
$event-&amp;gt;update($data);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting existing calendar event===&lt;br /&gt;
Simple example of deleting existing event from database.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$eventid = required_param(&#039;id&#039;, PARAM_INT);&lt;br /&gt;
$event = calendar_event::load($eventid);&lt;br /&gt;
$event-&amp;gt;delete($repeats);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[:en:Calendar|Calendar user docs]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Calendar_API&amp;diff=31473</id>
		<title>Calendar API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Calendar_API&amp;diff=31473"/>
		<updated>2012-01-16T07:43:34Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: /* The calendar_event class */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Calendar API allows you to add and modify events in the calendar for user, groups, courses, or the whole site.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The Moodle [[:en:Calendar|Calendar]] collects and displays calendar events from everything users have access to.&lt;br /&gt;
&lt;br /&gt;
If your plugin generates calendar events (such as due dates) then you need to add your events to the calendar.&lt;br /&gt;
&lt;br /&gt;
==File locations==&lt;br /&gt;
&lt;br /&gt;
All the calendar code is located in /calendar/lib.php.  You need to include this file in your script if you intend to use it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==The calendar_event class==&lt;br /&gt;
&lt;br /&gt;
In general functionality of calendar_event() class are to create, update and delete events.&lt;br /&gt;
&lt;br /&gt;
===Creating new event===&lt;br /&gt;
Creating new calendar event to database by defining some properties for the event.  &lt;br /&gt;
&lt;br /&gt;
If event hook is used, it will also call self::calendar_event_hook() to create event for the hook.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_event::create($properties)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating event===&lt;br /&gt;
Updating an existing event in database by providing at least an event id.  If the event is a repeated events, the rest of series event will also be updated (depending on the properties value of repeateditall).  This function could also be use to insert new event to database If the requested event is not exist in database.  The optional parameter $checkcapability is use to check user&#039;s capability to edit/add event.  By default $checkcapability parameter is set to true.  &lt;br /&gt;
&lt;br /&gt;
If event hook is used, it will also call self::calendar_event_hook() to update the hook event.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_event::update($data, $checkcapability=true)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting event===&lt;br /&gt;
Deleting an existing event in database.  The optional parameter $deleterepeated is use as indicator to remove the rest of repeated events.  The default value for $deleterepeated is true. Deleting an event will also deleting all associated files related to the event&#039;s editor context.&lt;br /&gt;
&lt;br /&gt;
If event hook is used, it will also call self::calendar_event_hook() to delete event for the hook.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_event::delete($deleterepeated=false)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Event hook===&lt;br /&gt;
The capability to use hook to perform specific action to calendar event.  This requires setting up $CFG-&amp;gt;calendar and include external calendar file in $CFG-&amp;gt;dirroot .&#039;/calendar/&#039;. $CFG-&amp;gt;calendar .&#039;/lib.php&#039;).&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
calendar_event_hook($action, array $args)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Examples==&lt;br /&gt;
The following are examples of using the basic calendar_event API in Moodle.&lt;br /&gt;
&lt;br /&gt;
===Example to create new event===&lt;br /&gt;
Creating new calendar event for closing feedback date.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$event = new stdClass;&lt;br /&gt;
$event-&amp;gt;name         = get_string(&#039;stop&#039;, &#039;feedback&#039;).&#039; &#039;.$feedback-&amp;gt;name;&lt;br /&gt;
$event-&amp;gt;description  = format_module_intro(&#039;feedback&#039;, $feedback, $feedback-&amp;gt;coursemodule);&lt;br /&gt;
$event-&amp;gt;courseid     = $feedback-&amp;gt;course;&lt;br /&gt;
$event-&amp;gt;groupid      = 0;&lt;br /&gt;
$event-&amp;gt;userid       = 0;&lt;br /&gt;
$event-&amp;gt;modulename   = &#039;feedback&#039;;&lt;br /&gt;
$event-&amp;gt;instance     = $feedback-&amp;gt;id;&lt;br /&gt;
$event-&amp;gt;eventtype    = &#039;close&#039;;&lt;br /&gt;
$event-&amp;gt;timestart    = $feedback-&amp;gt;timeclose;&lt;br /&gt;
$event-&amp;gt;visible      = instance_is_visible(&#039;feedback&#039;, $feedback);&lt;br /&gt;
$event-&amp;gt;timeduration = 0;&lt;br /&gt;
&lt;br /&gt;
calendar_event::create($event);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating existing calendar event===&lt;br /&gt;
Simple example of updating exiting event through moodle form.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$eventid = required_param(&#039;id&#039;, PARAM_INT);&lt;br /&gt;
$event = calendar_event::load($eventid);&lt;br /&gt;
&lt;br /&gt;
$data = $mform-&amp;gt;get_data();&lt;br /&gt;
$event-&amp;gt;update($data);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting existing calendar event===&lt;br /&gt;
Simple example of deleting existing event from database.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$eventid = required_param(&#039;id&#039;, PARAM_INT);&lt;br /&gt;
$event = calendar_event::load($eventid);&lt;br /&gt;
$event-&amp;gt;delete($repeats);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[:en:Calendar|Calendar user docs]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Calendar_API&amp;diff=31468</id>
		<title>Calendar API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Calendar_API&amp;diff=31468"/>
		<updated>2012-01-16T07:20:29Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: /* Examples */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Calendar API allows you to add and modify events in the calendar for user, groups, courses, or the whole site.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The Moodle [[:en:Calendar|Calendar]] collects and displays calendar events from everything users have access to.&lt;br /&gt;
&lt;br /&gt;
If your plugin generates calendar events (such as due dates) then you need to add your events to the calendar.&lt;br /&gt;
&lt;br /&gt;
==File locations==&lt;br /&gt;
&lt;br /&gt;
All the calendar code is located in /calendar/lib.php.  You need to include this file in your script if you intend to use it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==The calendar_event class==&lt;br /&gt;
&lt;br /&gt;
In general functionality of calendar_event() class are to create, update and delete events.&lt;br /&gt;
&lt;br /&gt;
===Creating new event===&lt;br /&gt;
Creating new calendar event to database by defining some properties for the event.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
calendar_event::create($properties)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating event===&lt;br /&gt;
Updating an existing event in database by providing at least an event id.  If the event is a repeated events, the rest of series event will also be updated (depending on the properties value of repeateditall).  This function could also be use to insert new event to database If the requested event is not exist in database.  The optional parameter $checkcapability is use to check user&#039;s capability to edit/add event.  By default $checkcapability parameter is set to true.  &lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
calendar_event::update($data, $checkcapability=true)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting event===&lt;br /&gt;
Deleting an existing event in database.  The optional parameter $deleterepeated is use as indicator to remove the rest of repeated events.  The default value for $deleterepeated is true. Deleting an event will also deleting all associated files related to the event&#039;s editor context.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
calendar_event::delete($deleterepeated=false)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Examples==&lt;br /&gt;
The following are examples of using the basic calendar_event API in Moodle.&lt;br /&gt;
&lt;br /&gt;
===Example to create new event===&lt;br /&gt;
Creating new calendar event for closing feedback date.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$event = new stdClass;&lt;br /&gt;
$event-&amp;gt;name         = get_string(&#039;stop&#039;, &#039;feedback&#039;).&#039; &#039;.$feedback-&amp;gt;name;&lt;br /&gt;
$event-&amp;gt;description  = format_module_intro(&#039;feedback&#039;, $feedback, $feedback-&amp;gt;coursemodule);&lt;br /&gt;
$event-&amp;gt;courseid     = $feedback-&amp;gt;course;&lt;br /&gt;
$event-&amp;gt;groupid      = 0;&lt;br /&gt;
$event-&amp;gt;userid       = 0;&lt;br /&gt;
$event-&amp;gt;modulename   = &#039;feedback&#039;;&lt;br /&gt;
$event-&amp;gt;instance     = $feedback-&amp;gt;id;&lt;br /&gt;
$event-&amp;gt;eventtype    = &#039;close&#039;;&lt;br /&gt;
$event-&amp;gt;timestart    = $feedback-&amp;gt;timeclose;&lt;br /&gt;
$event-&amp;gt;visible      = instance_is_visible(&#039;feedback&#039;, $feedback);&lt;br /&gt;
$event-&amp;gt;timeduration = 0;&lt;br /&gt;
&lt;br /&gt;
calendar_event::create($event);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating existing calendar event===&lt;br /&gt;
Simple example of updating exiting event through moodle form.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$eventid = required_param(&#039;id&#039;, PARAM_INT);&lt;br /&gt;
$event = calendar_event::load($eventid);&lt;br /&gt;
&lt;br /&gt;
$data = $mform-&amp;gt;get_data();&lt;br /&gt;
$event-&amp;gt;update($data);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting existing calendar event===&lt;br /&gt;
Simple example of deleting existing event from database.&lt;br /&gt;
&amp;lt;code php&amp;gt;&lt;br /&gt;
$eventid = required_param(&#039;id&#039;, PARAM_INT);&lt;br /&gt;
$event = calendar_event::load($eventid);&lt;br /&gt;
$event-&amp;gt;delete($repeats);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[:en:Calendar|Calendar user docs]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
	<entry>
		<id>https://docs.moodle.org/dev/index.php?title=Calendar_API&amp;diff=31464</id>
		<title>Calendar API</title>
		<link rel="alternate" type="text/html" href="https://docs.moodle.org/dev/index.php?title=Calendar_API&amp;diff=31464"/>
		<updated>2012-01-16T06:57:55Z</updated>

		<summary type="html">&lt;p&gt;Rwijaya: /* The calendar_event class */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;The Calendar API allows you to add and modify events in the calendar for user, groups, courses, or the whole site.&lt;br /&gt;
&lt;br /&gt;
==Overview==&lt;br /&gt;
&lt;br /&gt;
The Moodle [[:en:Calendar|Calendar]] collects and displays calendar events from everything users have access to.&lt;br /&gt;
&lt;br /&gt;
If your plugin generates calendar events (such as due dates) then you need to add your events to the calendar.&lt;br /&gt;
&lt;br /&gt;
==File locations==&lt;br /&gt;
&lt;br /&gt;
All the calendar code is located in /calendar/lib.php.  You need to include this file in your script if you intend to use it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
==The calendar_event class==&lt;br /&gt;
&lt;br /&gt;
In general functionality of calendar_event() class are to create, update and delete events.&lt;br /&gt;
&lt;br /&gt;
===Creating new event===&lt;br /&gt;
Creating new calendar event to database by defining some properties for the event.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
calendar_event::create($properties)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Updating event===&lt;br /&gt;
Updating an existing event in database by providing at least an event id.  If the event is a repeated events, the rest of series event will also be updated (depending on the properties value of repeateditall).  This function could also be use to insert new event to database If the requested event is not exist in database.  The optional parameter $checkcapability is use to check user&#039;s capability to edit/add event.  By default $checkcapability parameter is set to true.  &lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
calendar_event::update($data, $checkcapability=true)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Deleting event===&lt;br /&gt;
Deleting an existing event in database.  The optional parameter $deleterepeated is use as indicator to remove the rest of repeated events.  The default value for $deleterepeated is true. Deleting an event will also deleting all associated files related to the event&#039;s editor context.&lt;br /&gt;
&amp;lt;code&amp;gt;&lt;br /&gt;
calendar_event::delete($deleterepeated=false)&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==Examples==&lt;br /&gt;
&lt;br /&gt;
Adding an event to the calendar&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Dealing with files&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
etc&lt;br /&gt;
&lt;br /&gt;
==See also==&lt;br /&gt;
&lt;br /&gt;
* [[:en:Calendar|Calendar user docs]]&lt;/div&gt;</summary>
		<author><name>Rwijaya</name></author>
	</entry>
</feed>