From a3943ef78aaa2da3eba8d26ed7f51494f7a4111e Mon Sep 17 00:00:00 2001 From: Safat Date: Wed, 6 Nov 2024 10:10:40 +1100 Subject: [PATCH] Plugintype and API docs for SMS subsystem [4.5] --- .../version-4.5/apis/plugintypes/sms/index.md | 125 ++++++++++++++++++ .../version-4.5/apis/subsystems/sms/index.md | 83 +++++++++++- 2 files changed, 206 insertions(+), 2 deletions(-) create mode 100644 versioned_docs/version-4.5/apis/plugintypes/sms/index.md diff --git a/versioned_docs/version-4.5/apis/plugintypes/sms/index.md b/versioned_docs/version-4.5/apis/plugintypes/sms/index.md new file mode 100644 index 0000000000..c8f0d25948 --- /dev/null +++ b/versioned_docs/version-4.5/apis/plugintypes/sms/index.md @@ -0,0 +1,125 @@ +--- +title: SMS gateway +tags: + - SMS + - Gateway + - Notification +--- + + + +SMS gateway plugins allow you to create SMS gateway providers. +Providers are an interface between [SMS API](/apis/subsystems/sms/index.md) and the external SMS provider (e.g. Amazon Web Services). +This allows for the sending of SMS notifications to users from your Moodle instance. + +For example, you set up MFA (Multi-Factor Authentication) in Moodle and choose 'AWS' as your SMS gateway provider. +This enables users to receive SMS notifications as part of the authentication process. + +## File structure + +SMS gateway plugins are located in the `/sms/gateway` directory. A plugin should not include any custom files outside its own +plugin folder. + +Each plugin is in a separate subdirectory and consists of a number of mandatory files and any other files the developer is going to use. See the [common plugin files](/apis/commonfiles/index.mdx) documentation for other files which may be useful in your plugin. + +
+ The directory layout for the `smsgateway` plugin. + +```console +sms/gateway/example +├── classes +│   ├── gateway.php +│   ├── hook_listener.php +│   └── privacy +│   └── provider.php +├── lang +│   └── en +│   └── smsgateway_example.php +├── settings.php +└── version.php +``` + +
+ +## Key files + +There are a number of key files within the SMS gateway plugin which will need to be configured for correct functionality. + +- gateway.php +- hook_listener.php + +### gateway.php + +Each plugin must create a class called `gateway` which extends the `\core_sms\gateway` class. +The SMS API will use the extended methods from this class. + +```php title="Implementing the base SMS gateway" + +class gateway extends \core_sms\gateway { + + #[\Override] + public function send( + message $message, + ): message { + // Sample code to send an SMS message. + $config = (object)json_decode($awsconfig, true, 512, JSON_THROW_ON_ERROR); + $class = '\smsgateway_aws\local\service\\' . $config->gateway; + $recipientnumber = manager::format_number( + phonenumber: $message->recipientnumber, + countrycode: isset($config->countrycode) ?? null, + ); + if (class_exists($class)) { + $status = call_user_func( + $class . '::send_sms_message', + $message->content, + $recipientnumber, + $config, + ); + } + return $message->with( + status: $status, + ); + } + + #[\Override] + public function get_send_priority(message $message): int { + return 50; + } +} + +``` + +### hook_listener.php + +[Hooks](/apis/core/hooks/index.md) can be dispatched from the SMS API which the plugin can then listened to. +It is necessary for plugins developers to assess these hooks and implement accordingly. + +#### after_sms_gateway_form_hook + +This hook will allow plugins to add required form fields to assist users in configuring their SMS gateway. + +```php title="Listener method for after_sms_gateway_form_hook" + +public static function set_form_definition_for_aws_sms_gateway(after_sms_gateway_form_hook $hook): void { + if ($hook->plugin !== 'smsgateway_example') { + return; + } + + $gateways = [ + 'smsgateway_example' => get_string('list', 'smsgateway_example'), + ]; + $mform->addElement( + 'select', + 'gateway', + get_string('gateway', 'smsgateway_example'), + $gateways, + ); +} + +``` + +:::info + +For a real plugin example, see the [AWS SMS Gateway plugin](https://github.com/moodle/moodle/tree/main/sms/gateway/aws). + +::: diff --git a/versioned_docs/version-4.5/apis/subsystems/sms/index.md b/versioned_docs/version-4.5/apis/subsystems/sms/index.md index c8680cbedd..1f98487506 100644 --- a/versioned_docs/version-4.5/apis/subsystems/sms/index.md +++ b/versioned_docs/version-4.5/apis/subsystems/sms/index.md @@ -1,10 +1,24 @@ --- title: SMS API +tags: + - SMS --- -The SMS API lets you send SMS messages using configured gateways, fetch messages that were previously sent, and check on their status. +The SMS API allows developers to implement SMS-related features into their plugins. +The subsystem contains an SMS Manager class `\core_sms\manager` which facilitates the actions performed by the API. + +Some of the actions made possible are: + +- Sending messages +- Fetching messages +- Checking the status of a message +- Getting SMS gateways. + +Currently, the design of the SMS API features the following plugin types: + +- [SMS gateway](../../apis/plugintypes/sms) ## Sending an SMS @@ -20,17 +34,32 @@ $message = \core\di::get(\core_sms\manager::class) recipientuserid: $user->id, issensitive: false, async: false, + gatewayid: 22, ); ``` :::info Message lengths -A single SMS sent by the API may consist of up to 480 UTF-8 characters. It is up to the message _gateway_ plugin to determine how this message is sent to the recipient. +A single SMS sent by the API may consist of up to 480 UTF-8 characters. It is up to the message _gateway plugin_ to determine how this message is sent to the recipient. Any message longer than the maximum length will be immediately rejected. ::: +### Parameter consideration while sending messages + +When sending a message it's important add the correct component and message type for record keeping purpose. One component can have many +different types of messages and those types should be clearly mentioned while sending the messages so that they are clear in reporting +and other locations. [MDL-80963](https://tracker.moodle.org/browse/MDL-81015) will be used to build a report for messages status. + +:::info Gateway ID + +The gatewayid is an optional parameter and its not required to mention it. If the plugin has a specific gateway selected as a part of +the config and or a specific gateway config needs to be used, then it can mention the gatewayid of that specific gateway config. Otherwise, +the SMS API will pick one according to the priority and use that to send the message. + +::: + ### Sending messages containing sensitive information When sending a message containing something like a 2FA login token, you should make use of the `issensitive` flag. @@ -39,6 +68,12 @@ Passing this flag prevents the SMS subsystem from storing the content of the mes The `send()` method return an instance of `\core_sms\message` which can be used to check on the message status. +### Sending messages Asynchronously + +Messages can not be sent asynchronously yet. The parameter is implemented but the feature is yet to be built. Also, +sensitive message can not be sent asynchronously. Please check [MDL-81015](https://tracker.moodle.org/browse/MDL-81015) fore more +information. + ## Fetching messages Every sent message is stored in the database for subsequent reporting, and to check statuses. @@ -122,3 +157,47 @@ graph TD GQ --> |Gateway failed to send the message| GF end ``` + +## Getting SMS gateways + +[SMS gateways](/apis/plugintypes/sms/index.md) are plugins that provide a way to interface with external SMS providers. +Once a gateway is configured, any component implementing the SMS API can get a list of gateways. + +```php title="Getting the list of enabled gateways" +$manager = \core\di::get(\core_sms\manager::class); +$gatewayrecords = $manager->get_gateway_records(); + +// It is also possible to filter the request. +$gatewayrecords = $manager->get_gateway_records(['id' => $id]); + +// To get all the enabled gateway instances. +$gatewayrecords = $manager->get_enabled_gateway_instances(); +``` + +## Important hooks + +The SMS API dispatches some [hooks](/apis/core/hooks/index.md) which should be considered when implemented by a plugin/component. + +- before_gateway_deleted +- before_gateway_disabled + +Before deleting or disabling an [SMS gateways](/apis/plugintypes/sms/index.md), these two hooks are dispatched from the SMS API. +This allows components that are actively using that gateway to stop the action, or do necessary cleanup. +Listening to these hooks is crucial to avoid data loss or accidental deletion when disabling an active gateway. + +```php title="Implement the hooks to check for usage before deletion or deactivation" + +public static function check_gateway_usage_in_example_plugin( + before_gateway_deleted|before_gateway_disabled $hook, +): void { + try { + $smsgatewayid = (int)get_config('example_plugin', 'smsgateway'); + if ($smsgatewayid && $smsgatewayid === (int)$hook->gateway->id) { + $hook->stop_propagation(); + } + } catch (\dml_exception $exception) { + $hook->stop_propagation(); + } +} + +```