In part two of the "Adding renderers and templates to your Moodle plugin" series, I will present how to use the renderer I created in part one in my plugin.
To summarize what I completed in part one, I created a renderer for my questionnaire module plugin using the classes/output/ structure and namespaces. This construction allows my classes to take advantage of Moodle's autoloading.
Damyon Wiese, one of the Moodle HQ leads, pointed out an error I made in part one. I showed a version of the renderer that used the class name "mod_questionnaire_renderer" without namespaces, in the file "renderer.php" located in the classes/output/ directory. Apparently, this will not work in a final solution. If I don't use namespaces, then that file must be located in either the plugin root or in classes/. In any case, it is always best to use the namespace solution that I finished with. I have updated Part 1 to reflect the correct structures.
Now I am going to edit the index.php file to use the renderer I created.
In the pre-renderer index file, I used PHP's echo statement with Moodle's global $OUTPUT object and html_writer helper class to display the screen output. The global $OUTPUT object is the global renderer object available to any script in Moodle. If a script is not using renderers, it will likely use this object to perform its necessary output operations. When a plugin uses its own renderer, it instantiates its own renderer object and uses it in place of $OUTPUT.
To instantiate my plugin's renderer, I use the get_renderer helper function. This function is part of the core moodle_page class, and can be accessed through the global $PAGE object, available to any Moodle script. Like the global $OUTPUT object, the global $PAGE object is instantiated at the start of every Moodle script through the normal setup functions. If a script specifically includes the main config.php file, it will already have direct access to these global objects. If not, it will need to specifically include them with a PHP global statement.
To get a renderer object for my plugin, I need to add the line:
$output = $PAGE->get_renderer('mod_questionnaire');
This function is responsible for locating my renderer and returning an instantiated object of my renderer's class. This is the function that looks for a class named mod_questionnaire_renderer in the plugin root directory, or the classes/ subdirectory, or one that can be autoloaded from the namespace mod_questionnaire\output namespace named renderer.
Once I have this, anywhere I would have used $OUTPUT, I will now use the plugin's $output instead.
So, I change:
echo $OUTPUT->header();
to
echo $output->header();
And:
echo $OUTPUT->footer();
to:
echo $output->footer();
Note that the renderer I created does not specifically contain the header() or footer() functions. These are inherited from the parent class, plugin_renderer_base, and are used as defined there. If I want to change what the output of these functions are, I can extend or replace them in my renderer. But, these are important functions that do much of the heavy lifting for page setup and completion, so it's best to just use the core methods.
The other output my index script currently does is create an html_table object, set it up, and output it using the html_writer helper class. Since my renderer now takes care of that, I'm going to remove all of those bits and pass the necessary data to the renderer instead. That actual content of the page will be output with the statement:
echo $output->render_index($headings, $align, $content);
The function render_index is the one new function I did create, and it is responsible for returning the complete page's HTML to the caller.
So, using my renderer, my index page code to perform the page output, is really handled by four statements:
$output = $PAGE->get_renderer('mod_questionnaire');
echo $output->header();
echo $output->render_index($headings, $align, $content);
echo $output->footer();
The rest of the script is really there to define the three variables passed to the render_index function.
To see the changes made, the original index.php file (before renderers) can be found here. The modified version using renderers can be found here. The diff comparison can be viewed here.
In the next post, I will add template usage to the renderer solution.
Thoughts on learning and education and how technology is changing the way we perceive and perform them.
Showing posts with label namespace. Show all posts
Showing posts with label namespace. Show all posts
Monday, January 9, 2017
Thursday, January 5, 2017
Part 1 - Setting up the Renderer
In part one of the "Adding renderers and templates to your Moodle plugin" series, I will present how to setup and add a renderer file to your plugin.
So let's begin adding renderers to a plugin. To make things easier, I have set up a fork of questionnaire in my github account and created a branch to work in. If you wish to work along, it is located at https://github.com/mchurchward/moodle-mod_questionnaire/tree/BUILD_RENDERERS. I have added tags to the various work points to see the progression, and I will highlight them at appropriate points.
The code I will begin with is found at https://github.com/mchurchward/moodle-mod_questionnaire/tree/STARTINGPOINT.
As I mentioned in the previous post, I am going to start by modifying the index.php file to use renderers rather than inline display code. To do this, I will need to create a renderer file.
To create a renderer, I need to create a file called renderer.php for my plugin, that contains a class that extends the core plugin_renderer_base class. My class will contain functions with names that define the output I wish to render and/or overrides to existing core render output functions. For this example, since I am rendering the index page, I will create a function called render_index.
The location of the file can be in one of two places in my plugin's file structure. The most common place currently is in the root of the plugin directory. At the time of this writing, most of the core module plugins use the root of their directory to hold this file (mod/lti is the only one that doesn't). The other location is within the subdirectory classes/output/.
My index page at the start is essentially a table, created and output using the core Moodle html_table class from the /lib/outputcomponents.php file. For now, I am just going to move the usage of the html_table class to my renderer. So I will create the file renderer.php with the class definition mod_questionnaire_renderer and the function render_index()in the root of my plugin.
Note the class name I have used: mod_questionnaire_renderer . This is so Moodle's rendering system can find it. When I use the rendering functions I define, Moodle will look for a class definition named "plugintype]_[pluginname]_renderer" using Moodle's standard frankenstyle plugin naming convention.
The render_index function will take three arguments to create the output table. The heading for the heading row, the alignment for all columns and the actual row/column data. The important code for this file looks like this (you can see the whole file here):
class mod_questionnaire_renderer extends plugin_renderer_base {
/**
* Renders the HTML for the index page.
* @param array $headings Headings for the display columns.
* @param array $align Alignment for each column.
* @param array $data All of the table data.
* @return string
*/
public function render_index($headings, $align, $data) {
$table = new html_table();
$table->head = $headings;
$table->align = $align;
$table->data = $data;
return html_writer::table($table);
}
}
Since the purpose of the renderer functions are to return the display code (HTML) to be output, all I have done for now is moved the output from the html_table class to here. Not terribly interesting, but it still provides the advantages of a renderer, such as overriding it with themes.
As I mentioned previously, the class naming convention allows Moodle's rendering system to locate my plugin's renderer. The mechanism for this is:
$myrenderer = $PAGE->get_renderer('mod_questionnaire');
When I modify the index.php file, a line like that will be what I will use to define the renderer object I will use to generate my output.
Before I move on, I want to change my renderer to use namespaces and class autoloading. Namespaces allow for my classes to be more specifically identified, allowing Moodle's autoloading system to find them much quicker. They also allow me to have class names identical to other class names in Moodle without causing a coding error. This is because the full class name also contains the namespace to identify it.
The classes/ subdirectory is a new structure released in 2.6. It provides a standard mechanism to provide the class definitions your plugin requires, and can contain any subdirectory structure you require. It also allows Moodle to use automatic class loading, meaning that files containing class definitions can be discovered by other scripts rather than having to be specifically included in the script. When autoloading is combined with namespace usage (more on that in a bit), the code may perform better and will be easier to maintain with fewer backward compatibility problems.
Moodle's renderer autoloading system automatically looks in both the classes/output/ space and the plugin root for plugin renderers. But, other class definitions will only be looked for in the classes/ structure.
To use autoloading and namespaces, I will move my renderer.php file from the plugin root to the classes/output/ directory.
In the newly moved file, before the first line of code, I need to put in my namespace definition. Like the frankenstyle name of my class, a namespace consists of the [plugintype]_[pluginname], followed by an optional functional name for your space. When I use a namespace, the file must be located in my classes/ subdirectory in order for the autoloading system to find it. Since I want to use namespaces, this is another reason why I located my renderer in classes/output rather than my plugin root. Moodle expects to find the renderer classes in the "output" namespace for a plugin, so my namespace declaration will look like this:
namespace mod_questionnaire\output;
Notice that the "output" portion corresponds with the "output" subdirectory of "classes". This is how namespaces work. Every part of a namespace declaration in Moodle separated by "\" is expected to be a subdirectory in the "classes/" structure.
Defining this namespace further allows me to change the name of my class. Currently, the class is named mod_questionnaire_renderer. Because I have now declared to be a part of the mod_questionnaire namespace, I no longer need the "mod_questionnaire" portion. My class must now be named just renderer.
With these changes, the code in my renderer now looks like this (full file can be found here):
namespace mod_questionnaire\output;
class renderer extends \plugin_renderer_base {
/**
* Renders the HTML for the index page.
* @param array $headings Headings for the display columns.
* @param array $align Alignment for each column.
* @param array $data All of the table data.
* @return string
*/
public function render_index($headings, $align, $data) {
$table = new \html_table();
$table->head = $headings;
$table->align = $align;
$table->data = $data;
return \html_writer::table($table);
}
}
Note one other important, but subtle change. There is a backslash ("\") in front of the plugin_renderer_base, the html_table and the html_writer statements. This defines the namespace for those constructs as the root (core) namespace. This is necessary, since I have specified that the code in this file is in the mod_questionnaire\output namespace. Unless otherwise specified, all definitions will be expected to be in that same namespace. Since those items are contained in other namespaces (namely Moodle core or "\"), I need to specifically identify where they can be found for my code to work.
In my next post, I will modify the index.php file to use the newly defined renderer.
So let's begin adding renderers to a plugin. To make things easier, I have set up a fork of questionnaire in my github account and created a branch to work in. If you wish to work along, it is located at https://github.com/mchurchward/moodle-mod_questionnaire/tree/BUILD_RENDERERS. I have added tags to the various work points to see the progression, and I will highlight them at appropriate points.
The code I will begin with is found at https://github.com/mchurchward/moodle-mod_questionnaire/tree/STARTINGPOINT.
As I mentioned in the previous post, I am going to start by modifying the index.php file to use renderers rather than inline display code. To do this, I will need to create a renderer file.
To create a renderer, I need to create a file called renderer.php for my plugin, that contains a class that extends the core plugin_renderer_base class. My class will contain functions with names that define the output I wish to render and/or overrides to existing core render output functions. For this example, since I am rendering the index page, I will create a function called render_index.
The location of the file can be in one of two places in my plugin's file structure. The most common place currently is in the root of the plugin directory. At the time of this writing, most of the core module plugins use the root of their directory to hold this file (mod/lti is the only one that doesn't). The other location is within the subdirectory classes/output/.
My index page at the start is essentially a table, created and output using the core Moodle html_table class from the /lib/outputcomponents.php file. For now, I am just going to move the usage of the html_table class to my renderer. So I will create the file renderer.php with the class definition mod_questionnaire_renderer and the function render_index()in the root of my plugin.
Note the class name I have used: mod_questionnaire_renderer . This is so Moodle's rendering system can find it. When I use the rendering functions I define, Moodle will look for a class definition named "plugintype]_[pluginname]_renderer" using Moodle's standard frankenstyle plugin naming convention.
The render_index function will take three arguments to create the output table. The heading for the heading row, the alignment for all columns and the actual row/column data. The important code for this file looks like this (you can see the whole file here):
class mod_questionnaire_renderer extends plugin_renderer_base {
/**
* Renders the HTML for the index page.
* @param array $headings Headings for the display columns.
* @param array $align Alignment for each column.
* @param array $data All of the table data.
* @return string
*/
public function render_index($headings, $align, $data) {
$table = new html_table();
$table->head = $headings;
$table->align = $align;
$table->data = $data;
return html_writer::table($table);
}
}
Since the purpose of the renderer functions are to return the display code (HTML) to be output, all I have done for now is moved the output from the html_table class to here. Not terribly interesting, but it still provides the advantages of a renderer, such as overriding it with themes.
As I mentioned previously, the class naming convention allows Moodle's rendering system to locate my plugin's renderer. The mechanism for this is:
$myrenderer = $PAGE->get_renderer('mod_questionnaire');
When I modify the index.php file, a line like that will be what I will use to define the renderer object I will use to generate my output.
Before I move on, I want to change my renderer to use namespaces and class autoloading. Namespaces allow for my classes to be more specifically identified, allowing Moodle's autoloading system to find them much quicker. They also allow me to have class names identical to other class names in Moodle without causing a coding error. This is because the full class name also contains the namespace to identify it.
The classes/ subdirectory is a new structure released in 2.6. It provides a standard mechanism to provide the class definitions your plugin requires, and can contain any subdirectory structure you require. It also allows Moodle to use automatic class loading, meaning that files containing class definitions can be discovered by other scripts rather than having to be specifically included in the script. When autoloading is combined with namespace usage (more on that in a bit), the code may perform better and will be easier to maintain with fewer backward compatibility problems.
Moodle's renderer autoloading system automatically looks in both the classes/output/ space and the plugin root for plugin renderers. But, other class definitions will only be looked for in the classes/ structure.
To use autoloading and namespaces, I will move my renderer.php file from the plugin root to the classes/output/ directory.
In the newly moved file, before the first line of code, I need to put in my namespace definition. Like the frankenstyle name of my class, a namespace consists of the [plugintype]_[pluginname], followed by an optional functional name for your space. When I use a namespace, the file must be located in my classes/ subdirectory in order for the autoloading system to find it. Since I want to use namespaces, this is another reason why I located my renderer in classes/output rather than my plugin root. Moodle expects to find the renderer classes in the "output" namespace for a plugin, so my namespace declaration will look like this:
namespace mod_questionnaire\output;
Notice that the "output" portion corresponds with the "output" subdirectory of "classes". This is how namespaces work. Every part of a namespace declaration in Moodle separated by "\" is expected to be a subdirectory in the "classes/" structure.
Defining this namespace further allows me to change the name of my class. Currently, the class is named mod_questionnaire_renderer. Because I have now declared to be a part of the mod_questionnaire namespace, I no longer need the "mod_questionnaire" portion. My class must now be named just renderer.
With these changes, the code in my renderer now looks like this (full file can be found here):
namespace mod_questionnaire\output;
class renderer extends \plugin_renderer_base {
/**
* Renders the HTML for the index page.
* @param array $headings Headings for the display columns.
* @param array $align Alignment for each column.
* @param array $data All of the table data.
* @return string
*/
public function render_index($headings, $align, $data) {
$table = new \html_table();
$table->head = $headings;
$table->align = $align;
$table->data = $data;
return \html_writer::table($table);
}
}
Note one other important, but subtle change. There is a backslash ("\") in front of the plugin_renderer_base, the html_table and the html_writer statements. This defines the namespace for those constructs as the root (core) namespace. This is necessary, since I have specified that the code in this file is in the mod_questionnaire\output namespace. Unless otherwise specified, all definitions will be expected to be in that same namespace. Since those items are contained in other namespaces (namely Moodle core or "\"), I need to specifically identify where they can be found for my code to work.
In my next post, I will modify the index.php file to use the newly defined renderer.
Labels:
community,
development,
Moodle,
moodle-plugins,
namespace,
POET,
renderer,
template
Subscribe to:
Posts (Atom)