Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Content bank content types: Difference between revisions

From MoodleDocs
No edit summary
 
(CAN_DOWNLOAD feature)
Line 192: Line 192:
</code>
</code>


This function defines the features your plugin implements. Right now the features a plugin could implement in the content bank are CAN_UPLOAD and CAN_EDIT. So your plugin should return an empty array, an array with one of those elements, or an array with both elements. CAN_UPLOAD and CAN_EDIT constants are defined in \core_contentbank\contenttype class.
This function defines the features your plugin implements. Right now the features a plugin could implement in the content bank are CAN_UPLOAD, CAN_EDIT and CAN_DOWNLOAD. So your plugin should return an empty array, an array with one of those elements, or an array with these elements. These constants are defined in \core_contentbank\contenttype class.


An example of the get_implemented_features function is as follows:
An example of the get_implemented_features function is as follows:
Line 203: Line 203:
     */
     */
     protected function get_implemented_features(): array {
     protected function get_implemented_features(): array {
         return [self::CAN_UPLOAD, self::CAN_EDIT];
         return [self::CAN_UPLOAD, self::CAN_EDIT, self::CAN_DOWNLOAD];
     }
     }
</code>
</code>

Revision as of 07:35, 13 May 2021

Introduction

Content bank content types reside in the /contentbank/contenttype directory.

Each plugin is in a separate subdirectory and consists of a number of 'mandatory files' and any other files the developer is going to use. Please note, any reference to <contenttypename> in this documentation should be replaced by the name of your content type.

Below is an example of the file structure for the h5p content type plugin.

Contenttype folder.png

Standard Files and their Functions

There are several files that are crucial to Moodle. These files are used to install your plugin and then integrate it into the Moodle system. Each file has a particular function, some of the files are not necessary and are only created when wanting to use the functionality it offers. Below are the list of most commonly used files.

DB Folder

access.php

This is where you define what capabilities your plugin will create. Note, if you add new capabilities to this file after your plugin has been installed you will need to increase the version number in your version.php file (discussed later) in order for them to be installed.

An example of the file is below

$capabilities = array(

   'contenttype/h5p:access' => array(
       'captype' => 'read',
       'contextlevel' => CONTEXT_COURSE,
       'archetypes' => array(
           'manager' => CAP_ALLOW,
           'coursecreator' => CAP_ALLOW,
           'editingteacher' => CAP_ALLOW,
       )
   ),

);

Every content type needs to implement a ‘contenttype/<contenttypename>:access’ capability. This capability controls the access to all the contents managed and controlled by this plugin.

If your content type plugin allows uploading files with a certain extension, you would need to create contenttype/<contenttypename>:upload’ capability. This capability controls the permissions to create content uploading a file.

If your content type plugin implements an editor and allows content creation or edition via that editor, you need to create contenttype/<contenttypename>:useeditor’ capability. This capability controls the permissions to create content and edit it using the implemented editor.

For consistency and ease of use for the teachers it is advisable to use the following archetypes with the contenttype/<contenttypename>:access capability
'contenttype/CONTENTTYPENAME:access' => array(

       'captype' => 'read',
       'contextlevel' => CONTEXT_COURSE,
       'archetypes' => array(
           'manager' => CAP_ALLOW,
           'coursecreator' => CAP_ALLOW,
           'editingteacher' => CAP_ALLOW,
       )
   )

install.xml

This file is used on installation of your plugin; it defines the associated database tables. Content bank already implements ‘contentbank_content’ table contains ‘configdata’ and ‘instanceid’ fields. Those fields are not used by the main content bank, so are free for the plugins to use them.

Those fields are there to make it easier to develop a new contenttype plugin with no new table, but you can of course create the new tables you need using the XMLDB_editor. Please note, in the XML file the table names are listed without the config.php prefix, this is automatically used when creating the tables and does not need to be specified.

You can use the ‘instanceid’ field as a foreign key to any other table your plugins needs, and ‘configdata’ for any information your plugin needs, such some JSON content. There is no need to use them; those fields are there just to help with your plugin.

The contentbank_content table definition is below

<FIELDS> <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/> <FIELD NAME="name" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="contenttype" TYPE="char" LENGTH="100" NOTNULL="true" SEQUENCE="false"/> <FIELD NAME="contextid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="References context.id."/> <FIELD NAME="instanceid" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="configdata" TYPE="text" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="usercreated" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The original author of the content"/> <FIELD NAME="usermodified" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/> <FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="true" DEFAULT="0" SEQUENCE="false"/> <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="false" DEFAULT="0" SEQUENCE="false"/> </FIELDS> <KEYS> <KEY NAME="primary" TYPE="primary" FIELDS="id"/> <KEY NAME="contextid" TYPE="foreign" FIELDS="contextid" REFTABLE="context" REFFIELDS="id"/> <KEY NAME="usermodified" TYPE="foreign" FIELDS="usermodified" REFTABLE="user" REFFIELDS="id"/> <KEY NAME="usercreated" TYPE="foreign" FIELDS="usercreated" REFTABLE="user" REFFIELDS="id"/> </KEYS> <INDEXES> <INDEX NAME="name" UNIQUE="false" FIELDS="name"/> <INDEX NAME="instance" UNIQUE="false" FIELDS="contextid, contenttype, instanceid"/> </INDEXES>

upgrade.php

This file handles upgrading the plugin to match the latest version. After creating a content type and using it extensively on your site (and others) you may want to extend the functionality of your plugin. In some cases you need to add new fields to your plugin’s tables, edit the existing indexes, default values, etc. This is where the upgrade.php script becomes used. The install.xml file is only executed once, that is when your plugin is first installed, so changing the schema in this file does not change the database structure for users who have already installed the plugin. So, to perform this upgrade you need to do three things.

1. Ensure the install.xml file implements the latest table structure.
2. Add the instructions for the upgrade.php files.
3. Update the version number in your version.php file.

In this example we are only adding two new columns to the database, so we can use the XMLDB_editor to change the install.xml file AND create the upgrade path.

An example of the upgrade.php file is as follows -

function xmldb_certificate_upgrade($oldversion=0) {

   if ($oldversion < 2020062901) {
       // Add new fields to certificate table.
       $table = new xmldb_table('certificate');
       $field = new xmldb_field('showcode');
       $field->set_attributes(XMLDB_TYPE_INTEGER, '1', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, '0', 'savecert');
       if (!$dbman->field_exists($table, $field)) {
           $dbman->add_field($table, $field);
       }
       // Add new fields to certificate_issues table.
       $table = new xmldb_table('certificate_issues');
       $field = new xmldb_field('code');
       $field->set_attributes(XMLDB_TYPE_CHAR, '50', null, null, null, null, 'certificateid');
       if (!$dbman->field_exists($table, $field)) {
           $dbman->add_field($table, $field);
       }

 

       upgrade_plugin_savepoint(true, 2020062901, 'contenttype', '<contenttypename>');
   }

}

Whenever you change the version in your version.php plugin Moodle will look to see if anything needs to be done. The version that is stored in the database is passed to the xmldb_<contenttypename>_upgrade function as the variable $oldversion in this case. In this example lets say that the initial version was 2020012000, since this is less than 2020062901 (the new value we put in the version.php file) we will execute the code in the if statement which will then update the version stored in the database meaning this if statement is never executed again. For more extensive details on this please see Upgrade_API.

Lang Folder

This is where you store any strings you are going to use in your plugin. Each language has a specific folder that needs to be created in order for it to be used with your plugin. In this case we are going to use the English language. A folder called en is created in your lang folder that contains a file called ‘contenttype_<contenttypename>.php’ that lists the translations of your string. For example, your plugin may have a 'H5P interactive content' description, rather than hard coding this term in your form, you use a placeholder that will then retrieve the appropriate string depending on the language being used on your Moodle site. There is one mandatory placeholder for plugins called 'pluginname' that Moodle will use when listing this plugin. To keep with Moodle standards you should order your strings alphabetically by the placeholder name.

$string['pluginname'] = 'H5P'; $string['description'] = 'H5P interactive content';

If you wanted to add the Basque translation you would create a folder called eu and add the file contenttype_<contenttypename>.php to it.

$string['pluginname'] = 'H5P'; $string['description'] = 'H5P eduki interaktiboa';

Now, when using the string 'description' we will use the Moodle function get_string instead, which will get the appropriate string depending on the language being used.

get_string('description', 'contenttype_h5p');

Classes folder

The classes folder contains all the classes are mandatory for the plugin, and any other class your plugin needs.

Privacy folder

You need to implement a privacy provider for your plugin defining the user data the plugin stores.

For more extensive details on this please see Privacy API.

An example of the a null privacy provider is as follows:

class provider implements \core_privacy\local\metadata\null_provider {

   /**
    * Get the language string identifier with the component's language
    * file to explain why this plugin stores no data.
    *
    * @return  string
    */
   public static function get_reason() : string {
       return 'privacy:metadata';
   }

}

contenttype.php

Contenttype.php file should contain a contentype class extends the main \core_contentbank\contenttype. You can overwrite the non final functions in your plugin as you need, but there are some abstract functions that are mandatory to implement.

get_implemented_features()

   /**
    * Return an array of implemented features by the plugins.
    *
    * @return array
    */
   abstract protected function get_implemented_features(): array;

This function defines the features your plugin implements. Right now the features a plugin could implement in the content bank are CAN_UPLOAD, CAN_EDIT and CAN_DOWNLOAD. So your plugin should return an empty array, an array with one of those elements, or an array with these elements. These constants are defined in \core_contentbank\contenttype class.

An example of the get_implemented_features function is as follows:

    /**
    * Return an array of implemented features by this plugin.
    *
    * @return array
    */
   protected function get_implemented_features(): array {
       return [self::CAN_UPLOAD, self::CAN_EDIT, self::CAN_DOWNLOAD];
   }

get_manageable_extensions()

    /**
    * Return an array of extensions the plugins could manage.
    *
    * @return array
    */
  abstract public function get_manageable_extensions(): array;

This function defines the file extensions (including the dot) your plugin can support.

An example of the get_manageable_extensions function is as follows:

    /**
    * Return an array of extensions this contenttype could manage.
    *
    * @return array
    */
   public function get_manageable_extensions(): array {
       return ['.h5p'];
   }

get_contenttype_types()

    /**
    * Returns the list of different types of the given content type.
    *
    * A content type can have one or more options for creating content. This method will report all of them or only the content
    * type itself if it has no other options.
    *
    * @return array An object for each type:
    *     - string typename: descriptive name of the type.
    *     - string typeeditorparams: params required by this content type editor.
    *     - url typeicon: this type icon.
    */
   abstract public function get_contenttype_types(): array;

In case your plugin support creation of different types of content, you should list all those types in this function.

This method is used to generate the “Add” menu inside the content bank.

An example of the get_contenttype_types function is as follows:

    /**
    * Returns the list of different types of the given content type.
    *
    * @return array
    */
   public function get_contenttype_types(): array {
       $type = new \stdClass();
       $type->typename = 'testable';
       return [$type];
   }

Controlling permissions

Some other functions are defined to control content users permissions. The main content bank defines the access via capabilities to the content, but your plugin can overwrite some of the functions to make that control more strict. The capabilities are already checked, but you can prohibit the access to a content based on other information of the content.

is_access_allowed: to restrict the view access to a user for an specific content.

    /**
    * Returns user has access capability for the content itself.
    *
    * @return bool     True if content could be accessed. False otherwise.
    */
   protected function is_access_allowed(): bool

is_upload_allowed: to restrict the uploading feature to an specific user for any reason your plugin wants to check.

    /**
    * Returns plugin allows uploading.
    *
    * @return bool     True if plugin allows uploading. False otherwise.
    */
   protected function is_upload_allowed(): bool 

is_delete_allowed: to prohibit a user to delete an specific content.

    /**
    * Returns if content allows deleting.
    *
    * @param  content $content The content to be deleted.
    * @return bool True if content allows uploading. False otherwise.
    */
   protected function is_delete_allowed(content $content): bool 

is_manage_allowed: to prohibit a user to manage (rename, etc.) an specific content.

    /**
    * Returns if content allows managing.
    *
    * @param  content $content The content to be managed.
    * @return bool True if content allows uploading. False otherwise.
    */
   protected function is_manage_allowed(content $content): bool 

is_edit_allowed: to prohibit a user to edit an specific content using the editor.

    /**
    * Returns plugin allows edition.
    *
    * @param  content $content The content to be edited.
    * @return bool     True if plugin allows edition. False otherwise.
    */
   protected function is_edit_allowed(?content $content): bool 


There are also some functions that you most likely will need to overwrite:

get_view_content()

    /**
    * Returns the HTML content to add to view.php visualizer.
    *
    * @param  content $content The content to be displayed.
    * @return string           HTML code to include in view.php.
    */
   public function get_view_content(content $content): string 

This function returns the HTML code to render the content supported by your plugin. By default returns an empty string, so your plugin most likely needs to overwrite it.

Notice that the main function is triggering the ‘contentbank_content_viewed’ event, so you will need to call to parent:get_view_content function when overwriting it.

get_icon()

    /**
    * Returns the HTML code to render the icon for content bank contents.
    *
    * @param  content $content The content to be displayed.
    * @return string               HTML code to render the icon
    */
   public function get_icon(content $content): string 

This function returns by default the ‘unknown’ icon, so to make the contents supported by your plugin easier to identify, would be good to overwrite this function and return a more personalized icon.

content.php

content.php file should contain a content class extends the main \core_contentbank\content. Even if you are not overwriting any of the functions, the \contenttype_<contenttypename>\content class must exists.

There is no abstract class that you must overwrite, but there is a function that will help you to manage permissions:

is_view_allowed: to prohibit a user to view an specific content.

    /**
    * Returns user has access permission for the content itself (based on what plugin needs).
    *
    * @return bool     True if content could be accessed. False otherwise.
    */
   public function is_view_allowed(): bool

And there are some useful functions that might help you implementing your plugin:

set_configdata() and get_configdata()

Setter and getter function for the ‘configdata’ field that is ready for your plugin to use.

set_instanceid() and get_ instanceid()

Setter and getter function for the ‘instanceid’ field that is ready for your plugin to reference any other table your plugin is using.

get_file()

The function that returns the file linked to the content. Null in case the content has not file linked.

    /**
    * Returns the $file related to this content.
    *
    * @return stored_file  File stored in content bank area related to the given itemid.
    * @throws \coding_exception if not loaded.
    */
   public function get_file(): ?stored_file 

get_file_url()

The function that returns the URL of the file linked to the content. Empty string in case the content has not file linked.

    /**
    * Returns the file url related to this content.
    *
    * @return string       URL of the file stored in content bank area related to the given itemid.
    * @throws \coding_exception if not loaded.
    */
   public function get_file_url(): string

form/editor.php

If your plugin implements an editor to create and edit content, a editor.php file must exists in the form directory, extending the \core_contentbank\form\edit_content class. This file is used when adding/editing a content to the content bank. It contains the elements that will be displayed on the form responsible for creating/editing a content supported by your plugin. We are extending a ‘moodleform’ class you can overwrite or extend as you need.

class editor extends core_contentbank\form\edit_content {  

   function definition() {
       global $CFG, $DB, $OUTPUT;

 

       $mform =& $this->_form;

 

       $mform->addElement('text', 'title', get_string('title', 'contenttype_<contenttypename>'), array('size'=>'64'));
       $mform->setType('title', PARAM_TEXT);
       $mform->addRule('title', null, 'required', null, 'client');

 

       $ynoptions = array(0 => get_string('no'),
                          1 => get_string('yes'));
       $mform->addElement('select', 'useit', get_string('useit', 'contenttype_<contenttypename>'), $ynoptions);
       $mform->setDefault('useit', 1);
       $mform->addHelpButton('useit', 'useit', 'contenttype_<contenttypename>');

 

       $this->add_action_buttons();
   }

}

The above example does not contain the full file, just enough to provide you with an idea. First we create a text element called 'title' that is required. Then we created another element that stores whether a user wishes to use the title or not with a default value of 1 and a help button explaining what this setting does. The add_action_buttons function adds the submit and cancel buttons to the form.

You can also add validation to this form, just like any other form in Moodle. For more information on how to create forms in Moodle see Form_API.

Test folder

This folder is not mandatory, but we strongly recommend to write automated tests for all your plugins and features to ensure everything is working ok and changes do not break anything. Testing is essential to make sure that developed code does what it is meant to do without causing new problems and automated testing is a very helpful tool for it.

For more information about automated testing in Moodle see Testing#Automated_testing.

version.php

The version.php file keeps track of the version of your plugin, and other attributes, such as what version of Moodle it requires. For a full list of the attributes please see version.php.