Introduction
Implementing this plugin will allow you to publish files to all kinds of external document repository systems (eg: mahara, flickr, picasa, etc).
Error handling
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)
Language packs
If your plugin isn't going into core, you will need a language structure inside it, for example, portfolio/type/foo/lang/$language/.
File access
All 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't actually get moved around while tests are running.
Example
Template
Please refer to portfolio goodledocs plugins (portfolio/googledocs/) as template reference to start with.
Naming convention
Plugin class name should start with portfolio_plugin_PLUGINNAME. This class should also extends from portfolio_plugin_push_base or portfolio_plugin_pull_base.
class portfolio_plugin_foo extends portfolio_plugin_push_base {}
For more information regarding naming convention, please refer to: Frankenstyle page
File structure
For the purposes of this tutorial, it will be assumed you want to create a plugin called "foo"
- Create a new directory in portfolio/ for the new plugin. The name for this directory should refer to plugin's name, for example: portfolio/foo.
- Create the standard version.php in here that you would normally create for most other plugin types in Moodle.
- Create a lib.php inside the plugin directory, for example: portfolio/foo/lib.php.
- Create the necessary subclass to make our plugin work. The subclass must be called portfolio_plugin_PLUGINNAME (eg: 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.
- Create file for plugin language strings:
- create directory lang/ and subdirectory $language/ within the plugin, for example: portfolio/foo/lang/$language/. English (en) language must exist as this language will be use as default language string.
- create php file using this naming convention, portfolio_PLUGINNAME.php, for example: /portfolio/foo/lang/en/portfolio_foo.php
- Optionally, this plugin could store some information in database. In order to store data to database, create db/ directory within the plugin directory, for example: portfolio/foo/db/. Also, create install.xml for the table schema. For further details using database table, please refer to: XMLDB editor and Database FAQ > XMLDB.
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.
Interfacing to APIs
Methods you must override
Static functions
get_name
Return a localized name for this plugin. Usually just something like:
get_string('pluginname', 'portfolio_yourplugin');
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's blindly obvious you must implement it ;) )
Class methods
prepare_package
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->get('exporter')->get_tempfiles, which returns an array of stored_file objects. You can zip files using $this->get('exporter')->zip_tempfiles. You must not use the files api directly.
send_package
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->get('exporter')->get_tempfiles() which returns an array of stored_file objects.
get_interactive_continue_url
Return a url to present to the user as a 'continue to their portfolio' 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
verify_file_request_params (pull only)
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.
expected_time
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'll be given the option (and in the case of the third, advised not to)
Methods you can override
Static functions
plugin_sanity_check
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.
has_admin_config
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.
admin_config_form
It'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't need to handle setting the data of the elements (when editing), that's done in the caller, using get_allowed_config. You can also override admin_config_validation.
admin_config_validation
This follows the exact same format as the validation() function in the moodleform object.
get_allowed_config
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.
allows_multiple_instances
By default, all plugins can have multiple instances configured. If it's only sensible for your plugin to be able to have one instance configured, override this to return false.
allows_multiple_exports
By default, all plugin support multiple export configured. However, if it require a redirect for authentication and doesn't support dynamic constructed urls to return to, overide this to return false.
file_mime_check
This function need to be override to return false, if the file type have restriction on mimetypes.
Class methods
supported_formats
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.
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.
has_user_config
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.
has_export_config
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.
user_config_form
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.
export_config_form
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.
user_config_validation
This follows the exact same format as the validation() function in the moodleform object.
export_config_validation
This follows the exact same format as the validation() function in the moodleform object.
get_export_summary
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.
steal_control
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're finished, return to $CFG->wwwroot/portfolio/add.php?postcontrol=1 and processing will continue. If you override this, it might be useful to also override post_control.
post_control
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's part of the request parameters, it's passed through to post_control, which stores whatever it needs before the next stage.
get_allowed_user_config
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.
get_allowed_export_config
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)
instance_sanity_check
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.
send_file (pull only)
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't implement this function as the file is retrieved via an xmlrpc request and sent back encrypted and base64 encoded.
cleanup
if you'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.
mnet_publishes (deprecated on Moodle 2.0)
return array of xmlrpc service methods for mnet (see mahara implementation for more detail)
get_static_continue_url
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.
For example, the mahara plugin generates an interactive continue url based on an mnet session, and it contains the mnet sessionid. The static continue url just contains the "wantsurl" part of that in mahara - eg artefact/file/ resolve_static_continue_url converts the static url into a new mnet jump session url.
resolve_static_continue_url
See get_static_continue_url.
Methods you shouldn't really override
Static functions
create_instance
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.
Class methods
__construct
Constructor for the plugin. Subclasses don't need to override this.
save
Saves data stored in the object back to the database and resets the dirty flag.
delete
Deletes this instance and all configuration data associated with it completely from the database
set_export_config
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's a bit funky.
Database tables
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 /portfolio/foo/lib/install.xml with the table schema contained within it.
To create the install.xml file, use the XMLDB editor. See Database FAQ > XMLDB for further details.
Up-to-date documentation on upgrading portfolio plugin, as well as providing new capabilities and events to the system, can be found under Installing and Upgrading Plugin Database Tables