Skip to content

Commit 800d798

Browse files
committed
Plugintype and API docs for SMS subsystem [4.5]
1 parent a1d5912 commit 800d798

File tree

2 files changed

+201
-2
lines changed

2 files changed

+201
-2
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
---
2+
title: SMS gateway
3+
tags:
4+
- SMS
5+
- Gateway
6+
- Notification
7+
---
8+
9+
<Since version="4.5" issueNumber="MDL-83406" />
10+
11+
SMS gateway plugins allow you to create SMS gateway providers.
12+
Providers are an interface between [SMS API](/apis/subsystems/sms/index.md) and the external SMS provider (e.g. Amazon Web Services).
13+
This allows for the sending of SMS notifications to users from your Moodle instance.
14+
15+
For example, you set up MFA (Multi-Factor Authentication) in Moodle and choose 'AWS' as your SMS gateway provider.
16+
This enables users to receive SMS notifications as part of the authentication process.
17+
18+
## File structure
19+
20+
SMS gateway plugins are located in the `/sms/gateway` directory. A plugin should not include any custom files outside its own
21+
plugin folder.
22+
23+
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.
24+
25+
<details>
26+
<summary>The directory layout for the `smsgateway` plugin.</summary>
27+
28+
```console
29+
sms/gateway/example
30+
├── classes
31+
│   ├── gateway.php
32+
│   ├── hook_listener.php
33+
│   └── privacy
34+
│   └── provider.php
35+
├── lang
36+
│   └── en
37+
│   └── smsgateway_example.php
38+
├── settings.php
39+
└── version.php
40+
```
41+
42+
</details>
43+
44+
## Key files
45+
46+
There are a number of key files within the SMS gateway plugin which will need to be configured for correct functionality.
47+
48+
- gateway.php
49+
- hook_listener.php
50+
51+
### gateway.php
52+
53+
Each plugin must create a class called `gateway` which extends the `\core_sms\gateway` class.
54+
The SMS API will use the extended methods from this class.
55+
56+
```php title="Implementing the base SMS gateway"
57+
58+
class gateway extends \core_sms\gateway {
59+
60+
#[\Override]
61+
public function send(
62+
message $message,
63+
): message {
64+
// Sample code to send an SMS message.
65+
$config = (object)json_decode($awsconfig, true, 512, JSON_THROW_ON_ERROR);
66+
$class = '\smsgateway_aws\local\service\\' . $config->gateway;
67+
$recipientnumber = manager::format_number(
68+
phonenumber: $message->recipientnumber,
69+
countrycode: isset($config->countrycode) ?? null,
70+
);
71+
if (class_exists($class)) {
72+
$status = call_user_func(
73+
$class . '::send_sms_message',
74+
$message->content,
75+
$recipientnumber,
76+
$config,
77+
);
78+
}
79+
return $message->with(
80+
status: $status,
81+
);
82+
}
83+
84+
#[\Override]
85+
public function get_send_priority(message $message): int {
86+
return 50;
87+
}
88+
}
89+
90+
```
91+
92+
### hook_listener.php
93+
94+
[Hooks](/apis/core/hooks/index.md) can be dispatched from the SMS API which the plugin can then listened to.
95+
It is necessary for plugins developers to assess these hooks and implement accordingly.
96+
97+
#### after_sms_gateway_form_hook
98+
99+
This hook will allow plugins to add required form fields to assist users in configuring their SMS gateway.
100+
101+
```php title="Listener method for after_sms_gateway_form_hook"
102+
103+
public static function set_form_definition_for_aws_sms_gateway(after_sms_gateway_form_hook $hook): void {
104+
if ($hook->plugin !== 'smsgateway_example') {
105+
return;
106+
}
107+
108+
$gateways = [
109+
'smsgateway_example' => get_string('list', 'smsgateway_example'),
110+
];
111+
$mform->addElement(
112+
'select',
113+
'gateway',
114+
get_string('gateway', 'smsgateway_example'),
115+
$gateways,
116+
);
117+
}
118+
119+
```
120+
121+
:::info
122+
123+
For a real plugin example, see the [AWS SMS Gateway plugin](https://github.com/moodle/moodle/tree/main/sms/gateway/aws).
124+
125+
:::

versioned_docs/version-4.5/apis/subsystems/sms/index.md

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,24 @@
11
---
22
title: SMS API
3+
tags:
4+
- SMS
35
---
46

57
<Since version="4.5" issueNumber="MDL-79808" />
68

7-
The SMS API lets you send SMS messages using configured gateways, fetch messages that were previously sent, and check on their status.
9+
The SMS API allows developers to implement SMS-related features into their plugins.
10+
The subsystem contains an SMS Manager class `\core_sms\manager` which facilitates the actions performed by the API.
11+
12+
Some of the actions made possible are:
13+
14+
- Sending messages
15+
- Fetching messages
16+
- Checking the status of a message
17+
- Getting SMS gateways.
18+
19+
Currently, the design of the SMS API features the following plugin types:
20+
21+
- [SMS gateway](../../apis/plugintypes/sms)
822

923
## Sending an SMS
1024

@@ -20,17 +34,28 @@ $message = \core\di::get(\core_sms\manager::class)
2034
recipientuserid: $user->id,
2135
issensitive: false,
2236
async: false,
37+
gatewayid: 22,
2338
);
2439
```
2540

2641
:::info Message lengths
2742

28-
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.
43+
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.
2944

3045
Any message longer than the maximum length will be immediately rejected.
3146

3247
:::
3348

49+
### Parameter consideration while sending messages
50+
51+
When sending a message it's important to add the correct `component` (e.g. `tool_mfa`) and `messagetype` (e.g. `mfa code`) for record keeping purpose.
52+
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
53+
in reporting and other locations. [MDL-80963](https://tracker.moodle.org/browse/MDL-80963) will be used to build a report for messages status.
54+
55+
The `gatewayid` is an optional parameter, and it's not required to mention it. If the plugin has a specific gateway selected as a part of
56+
the config and or a specific gateway config needs to be used, then it can mention the `gatewayid` of that specific gateway config. Otherwise,
57+
the SMS API will pick one according to the priority and use that to send the message.
58+
3459
### Sending messages containing sensitive information
3560

3661
When sending a message containing something like a 2FA login token, you should make use of the `issensitive` flag.
@@ -39,6 +64,11 @@ Passing this flag prevents the SMS subsystem from storing the content of the mes
3964

4065
The `send()` method return an instance of `\core_sms\message` which can be used to check on the message status.
4166

67+
### Sending messages asynchronously
68+
69+
Messages can not be sent asynchronously yet. The parameter has been implemented, but the feature is yet to be built
70+
(see [MDL-81015](https://tracker.moodle.org/browse/MDL-81015) for more information).
71+
4272
## Fetching messages
4373

4474
Every sent message is stored in the database for subsequent reporting, and to check statuses.
@@ -122,3 +152,47 @@ graph TD
122152
GQ --> |Gateway failed to send the message| GF
123153
end
124154
```
155+
156+
## Getting SMS gateways
157+
158+
[SMS gateways](/apis/plugintypes/sms/index.md) are plugins that provide a way to interface with external SMS providers.
159+
Once a gateway is configured, any component implementing the SMS API can get a list of gateways.
160+
161+
```php title="Getting the list of enabled gateways"
162+
$manager = \core\di::get(\core_sms\manager::class);
163+
$gatewayrecords = $manager->get_gateway_records();
164+
165+
// It is also possible to filter the request.
166+
$gatewayrecords = $manager->get_gateway_records(['id' => $id]);
167+
168+
// To get all the enabled gateway instances.
169+
$gatewayrecords = $manager->get_enabled_gateway_instances();
170+
```
171+
172+
## Important hooks
173+
174+
The SMS API dispatches some [hooks](/apis/core/hooks/index.md) which should be considered when implemented by a plugin/component.
175+
176+
- before_gateway_deleted
177+
- before_gateway_disabled
178+
179+
Before deleting or disabling an [SMS gateways](/apis/plugintypes/sms/index.md), these two hooks are dispatched from the SMS API.
180+
This allows components that are actively using that gateway to stop the action, or do necessary cleanup.
181+
Listening to these hooks is crucial to avoid data loss or accidental deletion when disabling an active gateway.
182+
183+
```php title="Implement the hooks to check for usage before deletion or deactivation"
184+
185+
public static function check_gateway_usage_in_example_plugin(
186+
before_gateway_deleted|before_gateway_disabled $hook,
187+
): void {
188+
try {
189+
$smsgatewayid = (int)get_config('example_plugin', 'smsgateway');
190+
if ($smsgatewayid && $smsgatewayid === (int)$hook->gateway->id) {
191+
$hook->stop_propagation();
192+
}
193+
} catch (\dml_exception $exception) {
194+
$hook->stop_propagation();
195+
}
196+
}
197+
198+
```

0 commit comments

Comments
 (0)