Plugin skeleton generator

From MoodleDocs

Generate plugin skeleton

The plugin will generate the skeleton files needed for a specific plugin type. The full functionality of the plugin can be accessed either via a web interface or the command line script.

To generate a skeleton plugin via the web interface:

  1. Proceed to Administration > Site administration > Development > Generate plugin skeleton
  2. Fill in the various plugin features then click on the "Download plugin skeleton button"
Generate plugin skeleton

To generate the plugin skeleton by using the command line interface:

  1. Create a recipe file in the YAML format, either by writing it from scratch or by editing the template recipe located at cli/example.yaml (recommended)
  2. Invoke the command line script at cli/generate.php:
 php cli/generate.php --recipe=recipe.yaml

This will generate the plugin in the Moodle path directory corresponding to the plugin type being generated.

Installation

This plugin has been tested to work with Moodle 3.1 and newer. There are no guarantess it will work with earlier versions.

General installation procedures are those common for all Moodle plugins: Installing plugins.

When downloading the plugin from the git repository tool_pluginskel there are several options available: cloning the repository, downloading the zip file and extracting it or using the zip file for the plugin install interface accessible at Administration > Site administration > Plugins > Install plugins.

If you choose to clone the repository, then you need to clone it into MOODLE_ROOT_DIRECTORY/admin/tool/pluginskel:

 git clone https://github.com/mudrd8mz/moodle-tool_pluginskel MOODLE_ROOT_DIRECTORY/admin/tool/pluginskel

replacing MOODLE_ROOT_DIRECTORY with the actual Moodle installation root directory path. The zip file should be extracted to the same location.

Keep in mind that cloning the repository also creates a hidden .git directory, which you may not want on live servers.

Note: If you decide to use the install plugin interface don't forget to rename the folder inside the archive to pluginskel.

The web interface

The web interface is accessible from Administration > Site administration > Development > Generate plugin skeleton. You are given a choice between filling in all the required fields manually, uploading a previously created recipe file or writing the recipe by hand in the text area (presumably by copying and pasting an existing recipe).

The second page of the web interface presents you with all the options needed to generate the complete plugin skeleton. There are help icons associated with each element, as well as links to the relevant Moodle documentation, to guide you through the creation process.

Plugin skeleton creation

After you have filled the necessary fields, you are given three options:

  1. You can generate the plugin files and download them by clicking on the Download plugin skeleton button. The files will be packaged as a zip archive.
  2. You can choose to generate and save a recipe file in the YAML format from the form input. This is accomplished by clicking on the Download recipe button.
  3. You can view and edit the recipe created from the form by clicking the Show recipe button. You will be taken to the edit recipe web page:
View and edit the recipe

It's worth noting that any changes you make here to the recipe will be reflected on the previous page if you click the Back button.

The command line script

The command line script is located at cli/generate.php inside the plugin installation directory (that is MOODLE_ROOT_DIRECTORY/admin/tool/pluginskel). The general syntax of the command line script is as follows:

 php cli/generate.php --recipe=RECIPE.YAML [--help] [--target-dir=DIR] [--target-moodle=DIR]

The available arguments are:

  • --help, -h - prints a message about how to use the script.
  • --recipe=RECIPE.YAML - the location of the recipe file, as a relative or absolute path to the file. Mandatory.
  • --loglevel=LOGLEVEL - the verbosity of the log message. The default is WARNING. The plugin uses the Monolog logging library, for a full list of the different log levels please examine the file vendor/monolog/monolog/src/Monolog/Logger.php located in the plugin directory.
  • --target-dir=DIR - the path to the directory where the plugin files will be generate (either relative or absolute). Optional. Inside DIR a directory with the component name will be created where all the plugin files will be placed. For example, when creating the plugin mod_newmodule:
   php cli/generate.php --recipe=mod_newmodule_recipe.yaml --target-dir=/tmp

the plugin files will be located at /tmp/newmodule/.

  • target-moodle=DIR - this will generate all the plugin files in the Moodle path directory corresponding to the plugin type being generated, but with DIR considered the Moodle root directory. Optional. For example, to generate the plugin tool_newtool:
   php cli/generate.php --recipe=tool_newtool_recipe.yaml --target-moodle=/tmp

the plugin file will be located at /tmp/admin/tool/newtool/.

Note that if not using neither --target-dir, nor --target-moodle the plugin files will be generated in the Moodle path directory in the current Moodle installation.

The recipe format

The recipe needs to be written in the YAML format. It is highly recommended to use the example recipe located at cli/example.yaml as a template for the plugin and edit or remove options as needed.

Conceptually, the recipe is split into three different parts:

  1. The first part represents the options needed to create the version.php file and the language strings file located at lang/en/<component>.php. These two files are mandatory for all plugins, regardless of the plugin type, and the options are declared at the top level of the recipe. These are:
    component, name, release, version, requies, maturiy, copyright, dependencies, lang_strings
  1. The second part consists of features that are common to (most) all plugin types. This in turn is split into two sub-parts:
    1. Options that have a boolean value and they control if a file is to be generated or not. These are defined under the features section of the recipe. They are:
      install, uninstall, settings, readme, license, upgrade, upgradelib
    2. Options that needed to be defined as an array (either associative or numerically indexed). They reside at the top level of the recipe and they are:
      capabilities, message_providers, cli_scripts, observers_events, mobile_addons, phpunit_tests
  1. The last part consists of plugin specific features. All features that correspond to a plugin type will be under the <componenttype>_features section of the recipe.

Explanation about what the features do as well as links to relevant Moodle documentation for each of the above options can be found on the web interface, by clicking the help icons.

Adding new features

The plugin has been designed to be easily expanded by adding new features. But first let's take a look at how the plugin generates the skeleton files from a recipe.

The generation process

The generation process is handled by the manager class located at classes/local/util/manager.php. The input for the manager class is the array representation of a recipe. The conversion from the YAML format must be done before invoking the manager.

Invoking the manager class to create a plugin skeleton is done following these steps:

  1. First, an instance of the class is created by passing a logger class to the static constructor manager::instance($logger).
  2. The recipe is parsed and saved by the manager by invoking the public method load_recipe($recipe). The argument is an array representation of the recipe.
  3. The file content are created and saved by using the function make(). No files are created on disk at this stage.
  4. Actually creating the files can be done by invoking the method write_files($targetdirectory). As an alternative, you can access the files' content by using get_files_content() (this approach is used when creating the PHPUnit test cases for the plugin).

Note that the manager does no validation (if it exists, if it's writable, etc.) on the $targetdirectory.

Each file content is generated from a template using Mustache. The Mustache templates are located in the skel/ directory. Responsible for making use of the Mustache engine to generate the files are skeleton classes located at classes/local/skel, and each instance of the skeleton class will generate one file from one template. The template to generate the file from and the recipe data for the template are specified when the skeleton class is instantiated inside the manager function prepare_file_skeleton().

Now let's take a closer look at what happens when the function make() is called:

  • The function prepare_files_skeletons() is invoked. This function will create the all the files' content and save it in the files class attribute. One entry in the files is composed of the file name and the instance of the skeleton class responsible for generating that file.
  • Inside the prepare_files_skeletons() function, files are queued for generation by invoking the function prepare_file_skeleton() based on the recipe options. To check if a given file should be generated there are two different functions: has_common_feature(), which deals with the features common to all plugin types, and has_component_features() which checks whether a component specific feature is present.
  • Once all the files have been added to the files list, the skeleton class method render() will be invoked for each skeleton class, and that will generate the content of each file.

Adding new features

To add a new feature, one must first decide the type feature: if it's a common feature (it applies to all plugins), or if it's a component specific feature (it applies only to a specific plugin type). See the recipe format for more details.

After this has been decided, the following steps are necessary:

  1. Create a Mustache template (or more) for the feature. If the feature is a component feature, then the template should go in the skel/file/<componenttype>/ directory, where componenttype is the plugin type. Otherwise, it should be in the skel/file/ directory.
  2. Choose or create a new skeleton class to render the template. If creating a new skeleton class please extend one of the following three skeleton classes: php_internal_file (for files that are internal to Moodle and should not be accessible from the web page, they have the expression defined('MOODLE_INTERNAL') || die() at the beginning of the file), php_web_file (these are intended to be used from the web interface and include the config.php file), php_cli_file (these are command line scripts and define the constant 'CLI_SCRIPT' to be true) or txt_file (which is a common text file, like the LICENSE.md or README.md files).
  3. Add code to the manager function prepare_plugin_skeleton() to make sure that the file(s) are queued for generation. Based on the feature type one of the functions has_component_feature() or has_common_feature() should return true if the feature is present in the recipe.
  4. (Optional, but highly recommended)Create PHPUnit tests for the new feature in the tests/ directory. You can use one of the existing tests as a template.
  5. To have the new feature present on the web interface:
    1. The feature should be added to one (and not both) of the static functions: manager->get_component_variables($component) or manager->get_features_variables(). Based on the code already present it should be self explanatory how that is done.
    2. Create the language strings for the new feature in the file lang/en/tool_pluginskel.php. At the very least a feature will require two strings: the human readable name for the feature and the help message for that feature. It should be obvious about how to do that from the strings already present there.
  6. That's all.

Example

Here's an example of how to add a feature to the plugin.

First, we decide on the feature type and name. We'll add a new common feature, "myfeature:, which will be located in the features section of the recipe (meaning it will just toggle the creation of some file). If enabled, the file myfeature.php will generated in the directory mydirectory/.

A simple recipe should look something like this:

component: local_test

## Human readable name of the plugin.
name: Example plugin

## Human readable release number.
release: "0.1.0"

## Plugin version number, e.g. 2016062100. Will be set to current date if left empty.
version: 2016121200

## Required Moodle version, e.g. 2015051100 or "2.9".
requires: "2.9"

## Plugin maturity level. Possible options are MATURIY_ALPHA, MATURITY_BETA,
## MATURITY_RC or MATURIY_STABLE.
maturity: MATURITY_BETA

## Copyright holder(s) of the generated files and classes.
copyright: 2016 Alexandru Elisei <alexandru.elisei@gmail.com>, David Mudrák <david@moodle.com>

features:
    myfeature:true

Next we create the template file at skel/file/myfeature.mustache. We will use the skeleton class php_internal_file for generating this file, but we are free to use any other base class, or create our own.

Now we modify the manager class to make sure the file is generated properly:

  • We make sure that the function has_common_feature() returns true when the feature is specified in the recipe file. By default, if a feature is set to true and it resides in the features section of the recipe the function will return true. As this is our case, we don't have to modify has_common_feature().
  • Next we queue the file for generation inside the prepare_files_skeletons() function:

protected function prepare_files_skeletons() {

   ...
   if ($this->has_common_feature('myfeature')) {
       $this->prepare_file_skeleton('mydirectory/myfeature.php', 'php_internal_file', 'myfeature');
   }
   ...

}

  • (Optional, but highly recommended) Create the PHPUnit test file tests/myfeature_test.php. You can use one of the existing test files as an example (readme_test.php is one of the simplest).
  • Now we have to make sure that we have access to this feature in the web interface. For that we modify the function get_features_variables():

public static function get_features_variables() {

   $featuresvars = array();
   $featuresvars[] = array('name' => 'myfeature', 'type' => 'boolean');
   ...

} This means that when constructing the recipe from the form data the value for this option will be converted to a boolean.

  • The last step is adding the language strings for the new variable. Add the following to the lang/en/tool_pluginskel.php file:

// The name of the feature is 'features_myfeature' because in the recipe the feature is under 'features'. $string['features_myfeature'] = 'The name of the feature on the web form'; $string['features_myfeature_help'] = 'Help message for myfeature'; $string['features_myfeature_link'] = 'Link to additional resources regarding the feature';

  • That's it, we're done.