Showing posts with label mustache. Show all posts
Showing posts with label mustache. Show all posts

Monday, March 5, 2018

Looking at Moodle's new plugin development API's - Part 1

David Mudrack, of Moodle HQ released a plugin called the "My Todo List" block. While functional, its real purpose is to show off the latest advanced coding techniques available in recent versions of Moodle. See his Tweet. From the readme:
The main purpose of this plugin is to demonstrate usage of advanced coding techniques available in recent Moodle versions. Most notably:
  • Rendering HTML output via Mustache templates.
  • AJAX based workflow of the elementary CRUD operations.
  • Organising JS into AMD modules.
  • Organising external functions into traits.
  • Low-level access to the database via persistent models.
  • Using exporters for handling the data structures in rendering and AJAX.
For this post, I will install and examine the code with the goal of learning better how to use these concepts.

While I have been a plugin developer for many years, I have not been able to keep up with all of the latest techniques to enhance the plugins I maintain. I am personally really interested in getting better with AJAX and AMD, as many of my plugins could benefit from these features. I have already experimented and released code with renderers and Mustache templates (see the series beginning here), so I won't go deep into those unless I see something new.

To begin, I fork David's repository into my own Github repository. This gives me place to install and play with the code. Once I have a local copy, I load it into my IDE (I use PhpStorm) and take a look at the code.

A quick perusal of the code shows that this is indeed a simple plugin, and uses the techniques I'm most interested in! The main block code file,  block_todo.php, contains five methods, four of which I'm familiar with from standard block development: init(), get_content(), specialization(), and applicable_formats(). But the fifth, get_required_javascript(), I am not familiar with and it is not defined on the block development page. I suspect this has to do with the AMD module feature. And, while that may be true, it appears that it calls the parent method, meaning this function is part of the block class. I need to do some searching to see why this function is there.

I search the development documentation on Moodledocs, and find this function referred to on the "jQuery pre2.9" page. So, it must be a function that has remained even with the new AMD module addition. I think I may want to look for the AMD Module documentation first.

I find a page in the Moodle development docs wiki on Javascript Modules. This appears to be the main documentation for using AMD in plugins. Skimming through I see that initializing an AMD javascript function is done with the $this->page->requires->js_call_amd() function. This is done in the ToDo block's get_required_javascript method.

A closer look at the parent block class, defines the get_required_javascript method as:
Allows the block to load any JS it requires into the page.
By default this function simply permits the user to dock the block if it is dockable.
So, I'm not sure that this function is actually required, but may just be a convenient place to call the js_call_amd function from. I search all of the core blocks for js_call_amd and find two, both of which call it from the get_required_javascript method. So this seems to be the place to do that. Maybe later, I'll play with that to see if that is a required way to do it.

In any case, the function passes the arguments:
'block_todo/control', 'init', ['instanceid' => $this->instance->id]
This coincides with the file amd/src/control.js, which is the expected location within a plugin to find the AMD javascript files.

Getting back to the function of the plugin, the main block get_content function, is a very simple function that does two things: gets a list of todo items, and outputs them on the screen. The technique uses one technique I'm familiar with, templates, and two I am unfamiliar with, Moodle persistents and Moodle exporters.

This is what I am seeing in get_content:
// Load the list of persistent todo item models from the database.
$items = block_todo\item::get_my_todo_items();
This code uses the persistent class, which appears to be a new technique to manage database data using CRUD techniques. It will be interesting to learn why this exists and when it should be used.
// Prepare the exporter of the todo items list.
$list = new block_todo\external\list_exporter([
    'instanceid' => $this->instance->id
,
], [
    'items' => $items,
    'context' => $this->context
,]);
This code uses exporter class and appears to be a technique to manage data passed in and out of web service functions, used by external applications and AJAX.
// Render the list using a template and exported data.
$this->content->text = $OUTPUT->render_from_template('block_todo/content',
    $list->export($OUTPUT));
This code is using a Mustache template to display the block content and utilizes the exporter class to send the data rather than renderers defined for the plugin, as I am used to. Again, I will need to discover why this technique is used.

I have some concepts to learn, so I install the block just to see what it does and how it works. After adding it to my dashboard page, I play with it, and see that it is as advertises, a simple "To Do" list.


Hovering over the controls shows no obvious links, and a quick perusal of the page code shows a form, but not a standard functioning Moodle form. It looks like all of the controls are using Javascript, AJAX and web services to do the actual work. This will be all new to me. :-)

Next post, I will start my functional learning.

Friday, January 13, 2017

Part 4 - Using the Template

In the previous posts, I created and used a renderer, and created a simple mustache template for my plugin. In part four of the "Adding renderers and templates to your Moodle plugin" series, I will present how to code the template classes and then use them in the renderer.

To use a template, I need a few different constructs.

Firstly, I need a new class that implements the core renderable and templatable classes. The file and class name should be the same as the template file name to make things easier. And the file must be located in the classes/output/ directory.

Since my template file is called indexpage.mustache, I create a new file called indexpage.php in the classes/output directory, and add a class called indexpage that implements \renderable and \templatable.  The class declaration must look like this (the entire file is available here):

    class indexpage implements \renderable, \templatable {
    }

The purpose of this script is to collect the output data for the template and make it available to the renderer. The only real required function is the export_for_template function, as renderers require that function to retrieve the data structure that is passed into the template. Other functions can be provided as necessary including those that process the data for the template. The code for the export function looks like this:

    public function export_for_template(\renderer_base $output) {
        $data = ['headings' => [], 'rows' => []];
        foreach ($this->headings as $key => $heading) {
            $data['headings'][$key] = $heading;
        }
        foreach ($this->rows as $row) {
            list($topic, $name, $responses, $type) = $row;
            $data['rows'][] =['topic' => $topic, 'name' => $name,
                'responses' => $responses, 'type' => $type];
        }
        return $data;
    }

For my version, I pass all the necessary data for the template to the constructor, and then process it for output in the export function. The class contains two property variables, $headings and $rows, which will hold the table headings and each row of content for the index page based on what is passed into the constructor. My export_for_template function processes these variables and creates the expected data construct for the template.

If you recall from the last post, my template expects a data structure with two top level elements: #headings and #rows. In the export_for_template function, I create a PHP array called $data, and add the keys 'headings' and 'rows' to it, each of which is initialized to an array. Then, I load up the headings array with a value for each 'title[n]' index and the rows array with an array for each content row containing the topic, name, responses and type elements. So after processing, the function returns a structure that looks something like this (for example):

    $data['headings' =>
             ['title1' => 'Topic',
              'title2' => 'Name',
              'title3' => 'Responses',
              'title4' => 'Questionnaire Type'
             ],
          'rows' =>
             [0 =>
                 ['topic' => 'Topic1',
                  'name' => 'Questionnaire1',
                  'responses' => 42,
                  'type' => 'Private'
                 ],
             [1 =>
                 ['topic' => 'Topic2',
                  'name' => 'Questionnaire2',
                  'responses' => 11,
                  'type' => 'Private'
                 ]
             ]
         ];

This structure is what gets sent to the template to meet its requirements for data.

Now that I have the necessary implementation of the core \templatable class, and a template, I need to code the renderer to use them. Currently, the renderer's render_index function, creates the HTML output from Moodle core helper functions, specifically the \html_table class. I need to change that so it uses the template and template class.

To do that, I change the declaration of the function to take one argument, a \templatable object. And I get rid of the current code, and replace it with the object's export_for_template function and a call to the renderer class function render_from_template. The new function looks like this (you can see the entire file here):

    public function render_indexpage(\templatable $indexpage) {
        $data = $indexpage->export_for_template($this);
        return $this->render_from_template('mod_questionnaire/indexpage', $data);
    }

The new render_index function expects an object created from the classes/output/indexpage.php::indexpage class. With that object, it will call the export_for_template function to get the data necessary to pass into the template. Then it will pass that data to the template to get the HTML output from the core render_from_template function. The render_from_template function takes two arguments: the template to use and the data to pass the template. In my case, the template to use is defined by the argument 'mod_questionnaire/indexpage'. The 'mod_questionnaire' portion tells the function that this is for an activity module plugin, and therefore the template should be found in the directory '/mod/questionnaire/templates/'. The 'indexpage' portion indicates that there should be file named 'indexpage.mustache' in that directory. After that, the function executes the necessary magic to pass the data into the template and return the final formatted HTML.

The final step I need to do in order to use all of this new template code is change the way I am using the renderer in the index.php file. In this case, I only need to change the code where I am actually using the renderer to output to the screen. I need to do two things: change the arguments being passed to the render_index function and create an indexpage templatable object for that function to use.

The old code looks like this:
    echo $output->render_index($headings, $align, $content);

The new code looks like this:
    $indexpage = new \mod_questionnaire\output\indexpage($headings, $content);
    echo $output->render_indexpage($indexpage);

Note, although I changed the function name from render_index to render_indexpage, it was not necessary. I changed the name only because I felt it better reflected the functionality.

I now have everything I need to output my plugin's index page using renderers and templates. And, other developers can now easily change the way that page display looks using alternate templates or renderers in their themes without having to change any of the plugin's code. The entire plugin's code using this template can be viewed here.

In my next post I will look at alternate coding strategies and how I used renderers and templates for the rest of the plugin's output code.

Wednesday, January 11, 2017

Part 3 - Setting up Templates

In part three of the "Adding renderers and templates to your Moodle plugin" series, I will present how to set up and begin using templates with your plugin renderer.

At this point, in parts one and two, I have created a renderer for my plugin's index page, and modified the output code for the index page to use the renderer.

There are a number of parts to implementing templates. I need a renderer. I did this in the earlier parts of the series. I need output classes based on Moodle's \renderable and \templatable classes. And I need Mustache templates to define the actual HTML output.

The first thing I am going to do is construct the mustache template. A mustache template is a file that looks very much like an HTML file. This makes it easy for designer/developers, familiar with HTML to create a layout display. They do not need knowledge of PHP or Moodle internals.

In my plugin, I need to create a new subdirectory called "templates" to hold the mustache templates I create.  Any template file I create requires the ".mustache" extension. Moodle's output system is set up to use the component name and the templates subdirectory name to locate any templates I specify in the specific functions. Since I am creating one for the index page, I create a file called templates/indexpage.mustache.

If you recall from the renderer posts, my index page was essentially an HTML table, listing the questionnaire instances in the course it was being accessed for. I used the html_writer::table function to generate the HTML of this table with the data I loaded. My new template needs to do something similar, except I will have to write the HTML for the table listing using HTML code in the mustache file.

The file I create contains the following HTML/Mustache code (you can see the whole file here):

<table class="generaltable">
  <thead>
    <tr>
{{#headings}}
      <th class="header" style="text-align:left;" scope="col">{{title1}}</th>
      <th class="header" style="text-align:left;" scope="col">{{title2}}</th>
      <th class="header" style="text-align:center;" scope="col">{{title3}}</th>
      <th class="header" style="text-align:left;" scope="col">{{title4}}</th>
{{/headings}}
    </tr>
  </thead>
  <tbody>
{{#rows}}
    <tr>
      <td class="cell" style="text-align:left;" scope="col">{{topic}}</td>
      <td class="cell" style="text-align:left;" scope="col">{{{name}}}</td>
      <td class="cell" style="text-align:center;" scope="col">{{responses}}</td>
      <td class="cell" style="text-align:left;" scope="col">{{type}}</td>
    </tr>
{{/rows}}
  </tbody>
</table>

You can see from this that the mustache template file looks very much like part of an HTML file, except for the portions contained within multiple brace brackets ("{{ }}"). Anything contained within the brace bracket structures, are mustache constructs that will be replaced by real data when the template is executed.

When a template is executed, it is passed a data structure. The data structure is where the template gets the real data to substitute in place of its brace tagged variables. Deconstructing this template, I have two main data structures: headings and rows. These structures are contained within opening and closing tag pairs, specifically:
    {{#headings}} {{/headings}}
    {{#rows}} {{/rows}}

Each of these structures contains other data defined by tags. The headings structure contains
{{title1}}, {{title2}}, {{title3}} and {{title4}}. The rows structure contains {{topic}}, {{name}}, {{responses}} and {{type}}. This level of the structure is the actual data. When the template is executed, it will expect a data structure that contains these structures passed into it.

Putting the data into section tags allows for some logic to occur within the template. If the section tag exists as a variable in the passed in data, then the output contained within the tag will be displayed. If the section tag does not exist, then the output contained within the tag will not be displayed. Additionally, if the section tag variable is an array, then the output within it will be repeated for each item of the section tag array. For my template, this is important for the rows variable, since there may be multiple rows of data.

In the case of the headings section, if the data contains no headings variable, a table row with no columns will be displayed in the <thead> section. In the case of the rows section, one row containing four columns each (one each of topic, name, responses and type) will be displayed for each element of the rows array. If rows is not present in the data, then no content rows will be displayed.

For this template, since I know I am always going to be passing in the table headings, I really don't need the #header section and structure at all. I could simply pass in the title variables as standalone variables in the template data structure. In that case, I would rewrite the table header section without the opening and closing header tags.

While I haven't discussed the code that constructs the data to be sent to the template yet, I just want to point out some details about how that data can look. For this, assume $data is the data variable I am constructing to pass to the template.

The template will expect the data that is passed in to be either an object with each template tag defined as a property (object variable), or as an array with each template tag defined as an array index. In the case of this template, the data structure expected will consist of either:
    $data->headings
    $data->rows
or:
    $data['headings']
    $data['rows']
or a combination of both.

The headings variable, likewise can be either an object or an array. So for example, either
headings['title1'] or headings->title1 will work successfully with the template data.

For this template, if there are multiple rows of data to be displayed, then the rows variable must be an array structure. Each array item can be either another array or an object however. So, for this template for example, we expect either rows[0]['topic'] or rows[0]->topic. If rows is not an array, there will only ever be one row displayed.

The data structure required is documented as a JSON structure in the template file as comments before the code.

One other thing to point out before I move on. You may have noticed that all of the template variable tags are enclosed in two brace brackets, except for one. The name variable is enclosed in three brace brackets - {{{name}}}.  When a variable enclosed in two brace brackets is substituted for the real data, it is HTML escaped. That means only text will be displayed, and any HTML constructs contained in that variable will be converted to escaped text and displayed verbatim rather than interpreted. When a variable is enclosed in three brace brackets is substituted for the real data, the data is included raw. That means the HTML will be interpreted and displayed accordingly. This is useful when you want to display a block of HTML formatted content.

In the case of my template, the name variable also contains a link to the actual questionnaire instance, so I want the HTML to be displayed as I passed it. In actuality, I should probably rewrite the template so that the pieces of the link are passed instead of the entire HTML structure. Generally speaking, it is safer and better practice to pass only unformatted text.

In the next post, I will begin modifying the renderer code to use the template.