Writing a SMS plugin

This article explains the basic steps required to create a SMS plugin for your student management system. Please see https://rogo-eassessment-docs.atlassian.net/wiki/spaces/ROGO/pages/26869764 for a list of known plugins.

Simply put a SMS plugin needs to implement the functions in the abstract class plugins_sms which in turn implements the abstract class plugins.

It is recommended (to save having to make changes to your plugin due to core changes) to use the API when enrolling students onto modules, creating modules etc.

 

The repository of the example SMS plugin described in the following article can be found here. It include a dummy web service.

Directory Structure

A SMS plugin should follow the structure:

plugin_example_sms/

admin

classes

cron

db

lang

 

  • The base directory contains

    • readme.MD

      • a markdown file containing information on the plugin and how to install it

        1 2 3 4 5 6 7 8 9 10 11 12 Title: plugin_example_sms Author: Dr Joseph Baxter <joseph.baxter@nottingham.ac.uk> Copyright: University of Nottingham 2021 onwards Description: plugin_example_sms was developed for the University of Nottingham. It is an example SMS plugin for Rogō to show the basics needed to connect to a student management system. Installation: 1. Extract plugin_example_sms archive into the plugins/SMS directory inside Rogō. 2. Install via the plugins/index.php admin screen.
    • version.php

      • a php file containing the version of the plugin and the minimum version of Rogo required for it to work correctly

        1 2 $this->version = '1.0.0'; $this->requires = '7.2.3';
  • The admin sub directory contains

    • the php files referenced in plugin class that are called by the Rogo UI when syncing with the SMS manually

  • The classes sub directory contains

    • the main class for the plugin

      • the class name matching the base directory name i.e. plugin_example_sms.class.php

  • The cron sub directory contains

    • the php files used to sync with the SMS via a scheduled cron job

  • The db sub directory contains

    • insert.sql

      • the script called on installation to setup the plugin settings.

    • update.x.x.x.sql

      • update scripts for additional settings

      • x.x.x matching the version of the plugin

  • The lang sub directory contains

    • sub directories for each suppored lang. ‘en’ as a minimum

    • each sub directory contains plugin_example_sms.lang.php file

      • this populates the $string array with plugin translations

The Code

The following snippets are from example main class file that implements all of the required functions for a SMS plugin.

Not all SMS functions are implemented in this example to keep it small. And indeed you may not need to implement all functions. We focus on enrolments as this the most likely functionality you will require.

Get Enrolments

This function is called wither by the plugins scheduled enrolment cron task or manually via the module details admin screen where module enrolments can by sync for this (and next) academic year.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 /** * Get enrolments for academic session * @params integer $session academic session to sync enrolments with * @params integer $externalid external system module id */ public function get_enrolments($session = null, $externalid = null) { // Check enabled. if (!$this->is_enabled() or !$this->is_configured('enrolment')) { return; } // Specific module. if (!empty($externalid)) { $args['externalid'] = $externalid; } // Set session if not provided. if (empty($session)) { $yearutils = new \yearutils($this->config->db); $session = $yearutils->get_current_session(); } $args = array('academic_session' => $session); $response = $this->callws($args); // 1. Parse response. $enrol = json_decode($response); // 2. Process enrolments with \api\modulemanagement $mm = new \api\modulemanagement($this->db); $smsimports = array(); $node = 1; foreach ($enrol as $enroldetails) { $currentenrols = array(); $details = \module_utils::get_full_details('external', $enroldetails->id, $this->db, plugin_example_sms::SMS); $moduleid = $details['idMod']; $smsimports[$moduleid]['enrolcount'] = 0; $smsimports[$moduleid]['enrolusers'] = ''; $smsimports[$moduleid]['unenrolcount'] = 0; $smsimports[$moduleid]['unenrolusers'] = ''; // Enrol. $params = array(); $params['moduleextid'] = $enroldetails->id; $params['moduleextsys'] = plugin_example_sms::SMS; $params['session'] = $session; foreach ($enroldetails->users as $users) { $currentenrols[$enroldetails->id][] = $users->id; $params['studentid'] = $users->id; $params['attempt'] = 1; $params['nodeid'] = $node; $node++; $response = $mm->enrol($params, $this->userid); if ($response['statuscode'] === 100) { $smsimports[$moduleid]['enrolcount']++; $smsimports[$moduleid]['enrolusers'] .= $users->username . ','; } } // Unenrol. $params = array(); $params['moduleextid'] = $enroldetails->id; $params['moduleextsys'] = plugin_example_sms::SMS; $params['session'] = $session; $membership = \module_utils::get_student_members($session, $moduleid, $this->db); foreach ($membership as $idx => $member) { if (!in_array($member['studentid'], $currentenrols[$enroldetails->id])) { $params['studentid'] = $member['studentid']; $params['nodeid'] = $node; $response = $mm->unenrol($params, $this->userid); $node++; if ($response['statuscode'] === 100) { $smsimports[$moduleid]['unenrolcount']++; $smsimports[$moduleid]['unenrolusers'] .= $member['username'] . ','; } } } // Update SMS import log table. $smsimports[$moduleid]['enrolusers'] = rtrim($smsimports[$moduleid]['enrolusers'], ','); $smsimports[$moduleid]['unenrolusers'] = rtrim($smsimports[$moduleid]['unenrolusers'], ','); if ($smsimports[$moduleid]['unenrolcount'] > 0 or $smsimports[$moduleid]['enrolcount'] > 0) { \module_utils::log_sms_imports( $moduleid, $smsimports[$moduleid]['enrolcount'], $smsimports[$moduleid]['enrolusers'], $smsimports[$moduleid]['unenrolcount'], $smsimports[$moduleid]['unenrolusers'], 'Example', $session, $this->db ); } } }

As can be seen in the code we call the SMS endpoint (callws function), interpret its response and use the Rogo API to enrol/remove students from modules (\api\modulemanagement class). We also update the SMS log.

Update Enrolments

This is a wrapper function that is called when a new module is added to Rogo. In this instance it calls the above get_enrolments function to sync enrolments with the SMS. And it also calls the get_modules function to sycs module information (not implemented in the example, but would be information such as module name).

1 2 3 4 5 6 7 8 9 10 11 12 13 14 /** * Update module in an academic session * Updates module details and enrolments * @params integer $externalid external system module id * @params integer $session academic session to sync enrolments with */ public function update_module_enrolments($externalid, $session) { if (!$this->is_enabled()) { return; } $this->get_modules($externalid, $session); $this->get_enrolments($session, $externalid); }

Support Check

This function checks is moudle imports are supported by the module by checking the url, blurb and tooltip definitions exists. If not supported the option to modules with the SMS is not shown on the Rogo module admin screen.

1 2 3 4 5 6 7 8 9 10 11 12 /** * Check if module import is supported by the plugin * @return array|bool import url and translation strings, false if module import not supported */ public function supports_module_import() { if ($this->is_configured('module') or $this->is_configured('enrolment')) { return array('url' => $this->config->get('cfg_root_path') . '/plugins/SMS/' . $this->plugin . '/admin/enrolment.php', 'blurb' => $this->strings['importmodules'], 'tooltip' => $this->strings['importmodulestooltip']); } else { return false; } }

Similarly the following funcs checks if module enrolments are supported.

1 2 3 4 5 6 7 8 9 10 11 12 /** * Check if enrolment import is supported by the plugin * @return array|bool false if enrolment import not supported */ public function supports_enrol_import() { if ($this->is_configured('enrolment')) { return true; } else { return false; } }

Get plugin name

Gets the name of the plugin, which is used to identify modules, courses etc associated with it within Rogo.

1 2 3 4 5 6 7 8 /** * Get name of sms * @return string name of sms */ public function get_name() { return self::SMS; }

Support functions

Whilst not required functions the below support functions may be helpful.

The above functions are using a function to the plugin which checks the functionality is available.

1 2 3 4 5 6 7 8 9 10 11 12 13 /** * Is the plugin function configured * @param stiring $function name of function * @return boolean true if configured */ private function is_configured($function) { $configured = $this->config->get_setting($this->plugin, 'enable_' . $function); if (!is_null($configured) and $configured == true) { return true; } return false; }

and a private function to check if the functionality is enabled.

1 2 3 4 5 6 7 8 9 10 11 12 /** * Is this plugin enabled * @return boolean true if enabled */ private function is_enabled() { $enabledplugins = \plugin_manager::get_plugin_type_enabled('plugin_' . $this->plugin_type); if (in_array($this->plugin, $enabledplugins)) { return true; } return false; }

Scheduled Tasks

Scripts can be added to the cron directory in the plugin to be called by the system cron. The script should simply call the relevant action i.e for enrolment

1 2 $sms = new plugin_example_sms(0); $sms->get_enrolments();

The Database

Settings for the plugin are stored in the core config table.