Wiki

Clone wiki

moodle-mod_courseguide / Design - plan advanced

ANALYSIS FOR THE DEVELOPMENT OF COURSE GUIDE

This document provides a course of action and advice for designing the new Moodle plugin "Course Guide" based on the existing plugin "Moderator Guide" which includes a lot of similar features.

General

Considering that the block already has a lot of the functionality we need, and has pre-existing tables containing the data. In order to avoid migrating the data from the block to another plugin, or other tables, we suggest that the block become the center piece of the API. Any other plugin which decides to leverage the "guide" functionalities will have to depend on the block.

This document, unless otherwise specified, targets changes to be made to the block to support the "Course guide" plugin.

Template manager

A template manager class will be responsible for fetching and creating the available templates. It will be extended by the plugins who need to built atop that API. Here is an example of the base manager.

/!\ Code was not run!

namespace block_moderator_guide;

abstract class template_manager_base {

    public function __construct($component, $contextid = SITEID);

    protected function get_options_table() {
        return null;
    }

    protected function get_options_fields() {
        return [];
    }

    protected function get_options_sql_fields() {
        $fields = [];
        foreach ($this->get_options_fields() as $field) {
            $fields[] = "o.{$field} as o.opt_{$field}";
        }
        return implode(', ', $fields);
    }

    public function get_templates($limittoorganisation = null, $includehidden = false) {
        global $DB;

        $fields = 't.*';
        $join = '';
        $optionstable = $this->get_options_table();
        if ($optionstable !== null) {
            $join = "LEFT JOIN {$optionstable} o ON o.templateid = t.id";
            $fields .= ', o.id AS opt_id, ' . $this->get_options_sql_fields();
        }

        $sql = "
            SELECT $fields FROM {block_mdrtr_guide_templates}
            $join
            WHERE contextid = :contextid AND component = :component
        ";
        $params = ['contextid' => $this->contextid, 'component' => this->component];

        if ($limittoorganisation !== null) {
            $sql .= " AND (t.organisation = :org OR t.organisation IS NULL) ";
            $params['org'] = $limittoorganisation;
        }

        if (!$includehidden) {
            $sql .= " AND t.hidden = :hidden ";
            $params['hidden'] = 0;
        }

        $records = $DB->get_records_sql($sql, $params);

        $templates = [];
        foreach ($records as $record) {
            list($normalfields, $options) = $this->split_record($record);
            $templates[] = $this->make_template($normalfields, $options);
        }

        return $templates;
    }

    protected function make_template($fields, $options) {
        return new template($fields, $options);
    }

    protected function split_record($record) {
        $normalfields = [];
        $options = [];
        foreach ($record as $key => $value) {
            if (strpos($key, 'opt_') === 0) {
                $options[substr($key, 4)] = $value
            } else {
                $normalfields[$key] = $value;
            }
        }
        return [$normalfields, $options];
    }

    // Create, or update the templat and its options.
    public function save(template $template);

    // Delete the template.
    public function delete(template $template);

}

Explanations

The manager gets initialised with a component because it will need to know what component to filter the templates on. At a later stage, we could envision having "global" templates which span across multiple components when the component is null.

We also added a contextid field in order to anticipate future needs. For instance, perhaps later we could create some templates in course categories so that only some courses will see them. At the moment the contextid will always be hardcoded to SITEID.

As some components may require more options than others, the manager is capable of joining on a different table to gather the additional options that it needs. So each plugin will likely override the base manager to extend some methods in order to add its own logic. Here after is the simplest example of a plugin's manager.

The manager is also responsible for saving the template and its options. Upon saving the manager will ensure that the component and contextid are properly set in the table. It will also ensure that the options are updated, or created.

namespace block_moderator_guide;

class template_manager extends template_manager_base {
    public function __construct() {
        parent::__construct('block_moderator_guide');
    }
}

Database structure

Templates

Fields:

  • id (int)
  • name (char)
  • organization (char)
  • description (text)
  • template (text)
  • templateformat (int)
  • timecreated (int)
  • timemodified (int)
  • hidden (int 1)
  • defaultguidename (char )
  • NEW: component (text 255)
  • NEW: contextid (int)

Indexes:

  • id as primary key
  • component as non-unique index
  • component + contextid as non-unique index

Templates options

Fields:

  • id (int)
  • templateid (int)

Additional fields will depend on the implementation in plugins.

Indexes:

  • id as primary key
  • templateid as unique index

Template class

The template class is responsible for creating the form and controlling whether the template can be edited or not. Plugins will extend the class to add their own logic such as the handling of the options. In order to use an other template class, a plugin can override the method "make_template" or their template manager.

namespace block_moderator_guide;

abstract class template_base {
    public function __construct($fields, $options);
    public function get_id();               // Return the database table ID.
    public function get_fields();
    public function get_options_id();       // Return the database options table ID when it has one.
    public function get_options();

    public function can_edit();             // Whether the current user can edit the template.

    abstract public function get_form();             // Get the form instance.
    abstract public function update_from_form_data($data);
}

class template extends template_base {
}

Guide class

The guide class is responsible for its own persistence, and rendering.

namespace block_moderator_guide;

class guide {

    public function render();
    public function save();
    public function delete();

}

Updated