Actions

Difference between revisions of "Developer Area/Core Subsystems/Artefact Plugins // Artefact Types"

From Mahara Wiki

< Developer Area‎ | Core Subsystems
 
Line 25: Line 25:
 
This file is the backbone of the whole plugin, and the most logical starting point. The first thing you must do when creating this file is write a Plugin subclass -''' PluginArtefactMyplugin''' extends''' PluginArtefac''''''t'''. Read the phpdocs for the Plugin base class in<font face="Courier New"> lib/mahara.php</font>, and PluginArtefact in <font face="Courier New">artefact/lib.php</font> to see exactly what all the methods are. Any methods you must implement will always be abstract. This it what a bare-essential class looks like:
 
This file is the backbone of the whole plugin, and the most logical starting point. The first thing you must do when creating this file is write a Plugin subclass -''' PluginArtefactMyplugin''' extends''' PluginArtefac''''''t'''. Read the phpdocs for the Plugin base class in<font face="Courier New"> lib/mahara.php</font>, and PluginArtefact in <font face="Courier New">artefact/lib.php</font> to see exactly what all the methods are. Any methods you must implement will always be abstract. This it what a bare-essential class looks like:
  
<code style="background-color: #cccccc">defined('INTERNAL') || die();<br /><br /> require_once('activity.php');<br /><br /> class PluginArtefactMyPlugin extends PluginArtefact {<br /><br /> public static function get_artefact_types() {<br /> return array(<br /> 'MyPlugin',<br /> );<br /> }<br /><br /> public static function get_block_types() {<br /> return array();<br /> }<br /><br /> public static function get_plugin_name() {<br /> return 'MyPlugin';<br /> }<br /><br /> public static function menu_items() {<br /> return array(<br /> array(<br /> 'path' =&gt; 'myportfolio/myplugin',<br /> 'url' =&gt; 'artefact/myplugin/',<br /> 'title' =&gt; get_string('MenuItemString', 'artefact.myplugin'),<br /> 'weight' =&gt; 20,<br /> ),<br /> );<br /> }<br /> }<br /><br /><br /> class ArtefactTypeMyPlugin extends ArtefactType {<br /> public function render_self($options) {<br /> return get_string('TestName', 'artefact.myplugin');<br /> }<br /><br /> public static function get_icon($options=null) {}<br /><br /> public static function is_singular() {<br /> return false;<br /> }<br /><br /> public static function get_links($id) {}<br /> }<br /></code>
+
<syntaxhighlight lang="php>
 +
defined('INTERNAL') || die();
 +
require_once('activity.php');
 +
class PluginArtefactMyPlugin extends PluginArtefact {
 +
    public static function get_artefact_types() {
 +
        return array(
 +
            'MyPlugin',
 +
        );
 +
    }
 +
 
 +
    public static function get_block_types() {
 +
        return array();
 +
    }
 +
 
 +
    public static function get_plugin_name() {
 +
        return 'MyPlugin';
 +
    }
 +
 
 +
    public static function menu_items() {
 +
        return array(
 +
            array(
 +
                'path' => 'myportfolio/myplugin',
 +
                'url' => 'artefact/myplugin/',
 +
                'title' => get_string('MenuItemString', 'artefact.myplugin'),
 +
                'weight' => 20,
 +
            ),
 +
        );
 +
    }
 +
}
 +
 
 +
class ArtefactTypeMyPlugin extends ArtefactType {
 +
    public function render_self($options) {
 +
        return get_string('TestName', 'artefact.myplugin');
 +
    }
 +
 
 +
    public static function get_icon($options=null) {}
 +
 
 +
    public static function is_singular() {
 +
        return false;
 +
    }
 +
 
 +
    public static function get_links($id) {}
 +
 
 +
}</syntaxhighlight>
  
 
The main two methods in this class you must implement are<font face="Courier New"> get_artefact_types </font>and<font face="Courier New"> get_blocktypes</font>. Below you will find a table to be used as a cheat sheet that contains all of the abstract methods, taken from <font face="Courier New">lib/mahara.php</font> and <font face="Courier New">artefact/lib.php</font> with their phpdoc.
 
The main two methods in this class you must implement are<font face="Courier New"> get_artefact_types </font>and<font face="Courier New"> get_blocktypes</font>. Below you will find a table to be used as a cheat sheet that contains all of the abstract methods, taken from <font face="Courier New">lib/mahara.php</font> and <font face="Courier New">artefact/lib.php</font> with their phpdoc.

Latest revision as of 15:15, 11 February 2022

Mahara's basic funtionality is comprised of Artefacts.

The basic unit of content in Mahara is called an artefact. All sorts of things are artefacts - blog posts & blogs, files, profile information and more. The artefact plugin type allows more to be added to the system, and as such is one of the most interesting plugin types. If you're thinking "I want to add X to the system" where X is something like wikis, calendars, polls etc, then you're probably going to be creating an artefact plugin to provide it.

Artefacts can belong to users, groups, institutions or the site itself. Some artefacts may make sense in the context of one of these but not the others - for example it might not make sense for users to have wikis (or maybe it might!), while it would make more sense for groups.

Basics of an Artefact Plugin

When Thinking about writing a new Artefact plugin, the first step is to realise that they are comprised of several different parts. There are some required items every artefact plugin must include. For the purposes of this document we're going to assume you're writing a new artefact plugin called 'Myplugin'.

The first step is to create the required file structure by making a new directory under htdocs/artefact/myplugin. This folder will contain all of your files and items, and the basic structure is as follows:

  • lib.php - Contains the class that implements the functionality of your Artefact. This is the main controller, and it must extend the abstract class PluginArtefact.
  • version.php - contains version information about the artefact.
  • index.php - should your plugin contain content that is manipulated by the user, this file contains the functions necessary to show them.
  • /theme -folder. This contains all of the Template files of your artefact, as well as all of the CSS and images needed in your plugin. You can also have multiple themes for your artefact. The default theme is always created under /theme/raw, but you can also create new themes by placing them in new folders under /theme. Read more about templates from their own article
  • /db -folder. This folder contains two files that define the database structure of your plugin.
    • install.xml - An xml-file that defines the structure of your database according to the built-in XMLDB definition.
    • upgrade.php - Used to make checks for changes in the version of the plugin/blocktype. Needed when old versions of the plugin/blocktype must be upgraded.
  • /lang -folder. Localization files for you artefact. Contains translations for strings used in the artefact under their own folder.
  • /blocktype -folder. If your artefacts make use of any blocktypes, they will go under here. Refer to the blocktype documentation to learn more about them.

Attached to this page is also an image illustrating this hierarchy

lib.php

This file is the backbone of the whole plugin, and the most logical starting point. The first thing you must do when creating this file is write a Plugin subclass -' PluginArtefactMyplugin extends PluginArtefac't. Read the phpdocs for the Plugin base class in lib/mahara.php, and PluginArtefact in artefact/lib.php to see exactly what all the methods are. Any methods you must implement will always be abstract. This it what a bare-essential class looks like:

defined('INTERNAL') || die();
require_once('activity.php');
class PluginArtefactMyPlugin extends PluginArtefact {
    public static function get_artefact_types() {
        return array(
            'MyPlugin',
        );
    }

    public static function get_block_types() {
        return array();
    }

    public static function get_plugin_name() {
        return 'MyPlugin';
    }

    public static function menu_items() {
        return array(
            array(
                'path' => 'myportfolio/myplugin',
                'url' => 'artefact/myplugin/',
                'title' => get_string('MenuItemString', 'artefact.myplugin'),
                'weight' => 20,
            ),
        );
    }
}

class ArtefactTypeMyPlugin extends ArtefactType {
    public function render_self($options) {
        return get_string('TestName', 'artefact.myplugin');
    }

    public static function get_icon($options=null) {}

    public static function is_singular() {
        return false;
    }

    public static function get_links($id) {}

}

The main two methods in this class you must implement are get_artefact_types and get_blocktypes. Below you will find a table to be used as a cheat sheet that contains all of the abstract methods, taken from lib/mahara.php and artefact/lib.php with their phpdoc.

Artefact Types

Artefact plugins are made up of any number of artefact types that behave slightly differently, but are related. For example, the file plugin contains three artefact types - file, folder, and image, that have slightly different properties. The blog plugin contains blog and blogpost. As blogpost is a child of blog, file and image are children of folder as well (in a logical sense, rather than in php).

Artefact plugins may also contain blocktype plugins. Blocks are how artefacts get into views, and it's really just presentation information with some configuration.

For your plugin, you tell Mahara which blocktypes and artefact types you're implementing in the above functions, and then you must write a class for each. More on embedded blocktypes later on in this documentation. Please also see the Blocktype documentation elsewhere in the wiki.

ArtefactType subclasses

For each artefact type that PluginArtefactMyplugin::get_artefact_types returns, you must write a class that subclasses ArtefactType. Again, the best way to understand all the methods in the class is to read the phpdocs for the parent class, in artefact/lib.php. Also, the methods you must implement are abstract again.

Abstract functions cheat sheets:

Listed below are the abstract functions of the parent classes that you absolutely must implement in your lib.php, if you extend said classes. Keep in mind that these are by no means the only functions you will need to override. They are listed here as a quick cheat sheet to get you going faster.

Abstract class PluginArtefact extends Plugin PHPDoc for the functions
public static abstract function get_artefact_types()

This function returns a list of classnames of artefact types this plugin provides.

  • @return array
public static abstract function get_block_types()

This function returns a list of classnames of block types this plugin provides they must match directories inside artefact/$name/blocktype

  • @abstract
  • @return array
public static abstract function get_plugin_name()

This function returns the name of the plugin.

  • @return string
Abstract class ArtefactType PHPDoc for the functions
public static abstract function get_icon($options=null)

Returns a URL for an icon for the appropriate artefact.

  • @param array $options Options for the artefact. The array MUST have the 'id' key, representing the ID of the artefact for which the icon is being generated. Other keys include 'size' for a [width]x[height] version of the icon, as opposed to the default 20x20, and 'view' for the id of the view in which the icon is being displayed.
  • @return string URL for the icon
public static abstract function is_singular() Whether a user will have exactly 0 or 1 of this artefact type.
public static abstract function get_links($id)

Returns a list of key => value pairs where the key is either '_default' or a langauge string, and value is a URL linking to that behaviour for this artefact type

  • @param integer This is the ID of the artefact being linked to

There are also some other very commonly used functions. The best way to learn what to use is of course to read the code of existing plugins, but here's a listing of some of them:

Other common functions found in lib.php Description
menu_items()
get_event_subscriptions()
get_activity_types()
postinst($prevversion)
view_export_extra_artefacts($viewids)
get_config(' root_name ') Used to get some folder location(for example 'docroot' returns location of the mahara folder on the local disk(/var/www/mahara) and 'wwwroot' returns url of the mahara folder in the www directory(http://localhost/mahara))
get_cron() Used to create new cron jobs from within the plugins (read more in cron section)
artefact_export_extra_artefacts($artefactids)

index.php

Should your artefact plugin be visible to your users, you must also include an index.php in your plug-in. This is the controller file that leads up to showing the user your views from the /theme -folder. Here is an example of an index.php:

define('INTERNAL', 1);
define('MENUITEM', 'myportfolio/myplugin');
define('SECTION_PLUGINTYPE', 'artefact');
define('SECTION_PLUGINNAME', 'myplugin');
define('SECTION_PAGE', 'index');

require(dirname(dirname(dirname(FILE))) . '/init.php');
define('TITLE', get_string('MenuItemString', 'artefact.myplugin'));

$indexstring = get_string('IndexPageString', 'artefact.myplugin');

$smarty = smarty();
$smarty->assign('indexstring', $indexstring);
$smarty->display('artefact:myplugin:index.tpl');

Blocktypes

For each block type that PluginArtefactMyplugin::get_block_types returns, you must do the following: 

Create artefact/myplugin/blocktype/theblockname/lib.php and write a class in it: PluginBlocktypeTheblockname extends PluginBlocktype. Again, all required methods are abstract and the best way to learn about this is to read the parent class.

PluginBlocktype Abstract cheat sheet

Listed in this cheat sheet are the abstract functions you need to implement in each class. Also listed are some other commonly found overriden functions.

Abstract class PluginBlocktype PHPDoc for the functions
public static abstract function get_title();

If this blocktype contains artefacts, and uses the artefactchooser Pieform element to choose them, this method must return the definition for the element. This is used in view/artefactchooser.json.php to build pagination for the element. The element returned MUST have the name key set to either 'artefactid' or 'artefactids', depending on whether 'selectone' is true or false. The element must also have the 'blocktype' key set to the name of the blocktype that the form is for.

  • @param mixed $default The default value for the element
public static abstract function get_description();

subclasses can override this if they need to do something a bit special eg more than just what the BlockInstance??->delete function does.

  • @param BlockInstance $instance
public static abstract function get_categories(); Returns array which specifies the categories where the blocktype resides in the view creator. Possbile options: feeds, fileimagevideo, general, internal, resume
public static abstract function render_instance(BlockInstance $instance, $editing=false); Returns String with formatted html code detailing what the plugin looks like in the view
public static abstract function artefactchooser_element($default=null) Combines artefact to blocktype
public static function delete_instance(BlockInstance $instance) { }
public static function instance_config_form(BlockInstance $instance) {} This function must be implemented in the subclass if it has config
public static function instance_config_validate(Pieform $form, $values) { } Blocktype plugins can implement this to perform custom pieform validation, should they need it
public static function get_artefacts(BlockInstance $instance) { }

Most blocktype plugins will attach to artefacts. They should implement this function to keep a list of which ones. The result of this method is used to populate the view_artefact table, and thus decide whether an artefact is in a view for the purposes of access.See {@link artefact_in_view} for more information about this. Note that it should just handle top level artefacts. The cache rebuilder will figure out the children.

  • @return array ids of artefacts in this block instance

Database tables and XMLDB (db/ -folder)

The database tables in Mahara are created and handled by XMLDB, which should be familiar to moodle developers. Each plugin gets its own install.xml and upgrade.php, as well as their own version.php. The functions of the files are as follows: 

  • install.xml - Mappings/Definitions for database tables and connections
  • upgrade.php - Used to make checks for changes in the version of the plugin. Needed when old versions of the plugin/blocktype must be upgraded. When upgrade.php is loaded, version.php is automatically read to get the latest version of the plugin.

To learn more about XMLDB, please refer to the moodle documentation of XMLDB and also the Mahara Database Conventions. It might also be useful to read an existing plugins databases.

Themes

Plugins often have the need to add their own Dwoo templates - this is done by having a theme/default/ directory and putting templates in it. You can also do this for blocktypes within the artefact plugin. Dwoo templates are derivative of older Smarty templates. To learn more, please read the adjacent article here.

Language strings

For all strings in your artefact plugin, you must use:

get_string('mystringkey', 'artefact_myplugin'); //or

get_string('mystring', 'blocktype.myplugin/theblockname');

You can place the strings inside artefact/myplugin/lang/en.utf8/artefact.myplugin.php and artefact/myplugin/blocktype/theblockname/lang/en.utf8/blocktype.theblockname.php .To learn more about language packs and translations, please refer to this article: Language Packs -> Structure

An example of a string file looks like the following:

defined('INTERNAL') || die();

$string['TestName'] = 'Testname works!'; $string['MenuItemString'] = 'MyPlugin!';
$string['IndexPageString'] = 'This is the index page. This string is fetched from the lang file of the plugin.';

Cron jobs

If your plug-in has tasks that need to be done regularly, you can instate a cron job to achieve that. Thanks to the embedded cron system in mahara, this is achieved relatively simply by overriding the get_cron() function and returning an array detailing the cron job. An example of this functionality would be:

public static function get_cron() {
return array(
(object)array(
'callfunction' => 'clean_email_validations',
'hour' => '4',
'minute' => '10',
),
);
}

public static function clean_email_validations() {
//do something here
}

Help pages

Help files can be in three differently named folders:

Forms(Artefact plugin):

These help files can be used from within the classes in an artefact plugins lib.php.
Name format: class_name.element_name.html
Example name: mytype.date.html OR mypieform.date.html
Example usage:

class ArtefactTypeMyType extends ArtefactTypeMyPluginComposite {
OR
$form = pieform(array(
'name' => 'MyPieform',
//...
'date' => array(
'type' => 'text',
'rules' => array(
'required' => true,
),
'title' => get_string('date', 'artefact.myplugin'),
'help' => true,
'size' => 20,
)

Forms(Blocktype plugin):

These help files can be used from within the instance_config_form() function
Name format: instconf.element_name.html
Example name: addbook.html
Example usage:

return array(
'width' => array(
'type' => 'text',
'title' => get_string('width','blocktype.myplugin/myblocktype'),
'size' => 3,
'rules' => array(
'required' => true,
'integer' => true,
),
'defaultvalue' => (empty($configdata['width']) ? $configdata['width'] : self::$default_width),
'help' => true,
)
);

Pages:

These help files are just named after the page they are used in. If the page name is index, then the index page will have the help beside its heading automatically.
Name format: page_name.html
Example name: index.html

Sections:

These help files can be used from within the *.tpl files in theme folder. Or in some case, from a pieform.
Name format: section_name.html
Example name: example.html
Example usage:

{contextualhelp plugintype='artefact' pluginname='myplugin' section='example'}

Usage from a pieform array:

You can set a browsehelp for a file browser element(used when uploading files into Mahara) in a pieform array like this: 'browsehelp' => 'browsemyfiles',
This way the file must be named browsemyfiles.html

Not yet documented

Event handling, notifications (see Plugin class), probably more. Add more here! :)

Files

  • Artefact plugin folder structure.png