Skip to content

Commit 8a4c8d1

Browse files
committed
Add more specific bounce handling
1 parent 8475b2e commit 8a4c8d1

12 files changed

+590
-52
lines changed

classes/check/bounces.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ public function get_result() : result {
6969
FROM {user_preferences} up
7070
LEFT JOIN {user_preferences} up2 ON up2.name = 'email_send_count' AND up.userid = up2.userid
7171
WHERE up.name = 'email_bounce_count' AND CAST(up.value AS INTEGER) > :threshold";
72-
// Start with a threshold of 1 as that is the default for manually created users.
73-
// TODO: Update when core is fixed.
72+
// Start with a threshold of 1 as that was a historical default for manually created users.
7473
$bounces = $DB->get_records_sql($sql, ['threshold' => 1]);
7574
$userswithbounces = count($bounces);
7675

classes/event/bounce_count_reset.php

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
// This file is part of Moodle - http://moodle.org/
3+
//
4+
// Moodle is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Moodle is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16+
17+
/**
18+
* Bounce count reset event
19+
*
20+
* @package tool_emailutils
21+
* @author Benjamin Walker ([email protected])
22+
* @copyright 2024 Catalyst IT
23+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24+
*/
25+
namespace tool_emailutils\event;
26+
27+
/**
28+
* Event
29+
*/
30+
class bounce_count_reset extends \core\event\base {
31+
32+
/**
33+
* Initialise required event data properties.
34+
*/
35+
protected function init(): void {
36+
$this->data['crud'] = 'u';
37+
$this->data['edulevel'] = self::LEVEL_OTHER;
38+
}
39+
40+
/**
41+
* Returns localised event name.
42+
*
43+
* @return string
44+
*/
45+
public static function get_name(): string {
46+
return get_string('event:bouncecountreset', 'tool_emailutils');
47+
}
48+
49+
/**
50+
* Returns non-localised description of what happened.
51+
*
52+
* @return string
53+
*/
54+
public function get_description(): string {
55+
$reason = empty($this->userid) ? 'because an email was delivered successfully' : 'manually';
56+
return "The user with id '$this->relateduserid' had their email bounce count reset $reason.";
57+
}
58+
}

classes/event/notification_received.php

+3-6
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
*/
2525
namespace tool_emailutils\event;
2626

27-
use tool_emailutils;
28-
2927
/**
3028
* Event
3129
*/
@@ -34,18 +32,17 @@ class notification_received extends \core\event\base {
3432
/**
3533
* Initialise required event data properties.
3634
*/
37-
protected function init() {
35+
protected function init(): void {
3836
$this->data['crud'] = 'u';
3937
$this->data['edulevel'] = self::LEVEL_OTHER;
40-
$this->data['objecttable'] = 'user';
4138
}
4239

4340
/**
4441
* Returns localised event name.
4542
*
4643
* @return string
4744
*/
48-
public static function get_name() {
45+
public static function get_name(): string {
4946
return get_string('event:notificationreceived', 'tool_emailutils');
5047
}
5148

@@ -54,7 +51,7 @@ public static function get_name() {
5451
*
5552
* @return string
5653
*/
57-
public function get_description() {
54+
public function get_description(): mixed {
5855
return $this->other;
5956
}
6057
}
+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
<?php
2+
// This file is part of Moodle - http://moodle.org/
3+
//
4+
// Moodle is free software: you can redistribute it and/or modify
5+
// it under the terms of the GNU General Public License as published by
6+
// the Free Software Foundation, either version 3 of the License, or
7+
// (at your option) any later version.
8+
//
9+
// Moodle is distributed in the hope that it will be useful,
10+
// but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
// GNU General Public License for more details.
13+
//
14+
// You should have received a copy of the GNU General Public License
15+
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
16+
17+
/**
18+
* Over bounce threshold event
19+
*
20+
* @package tool_emailutils
21+
* @author Benjamin Walker ([email protected])
22+
* @copyright 2024 Catalyst IT
23+
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24+
*/
25+
namespace tool_emailutils\event;
26+
27+
/**
28+
* Event
29+
*/
30+
class over_bounce_threshold extends \core\event\base {
31+
32+
/**
33+
* Initialise required event data properties.
34+
*/
35+
protected function init(): void {
36+
$this->data['crud'] = 'u';
37+
$this->data['edulevel'] = self::LEVEL_OTHER;
38+
}
39+
40+
/**
41+
* Returns localised event name.
42+
*
43+
* @return string
44+
*/
45+
public static function get_name(): string {
46+
return get_string('event:overbouncethreshold', 'tool_emailutils');
47+
}
48+
49+
/**
50+
* Returns non-localised description of what happened.
51+
*
52+
* @return string
53+
*/
54+
public function get_description(): string {
55+
return "The user with id '$this->relateduserid' is now over the bounce threshold.";
56+
}
57+
}

classes/helper.php

+36-2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
namespace tool_emailutils;
2828

29+
use tool_emailutils\event\bounce_count_reset;
30+
2931
/**
3032
* Helper class for tool_emailutils.
3133
*
@@ -67,7 +69,7 @@ public static function get_username_fields(string $tablealias = ''): string {
6769
*/
6870
public static function get_min_bounces(): int {
6971
global $CFG;
70-
return $CFG->minbounce ?? self::DEFAULT_MIN_BOUNCES;
72+
return empty($CFG->minbounces) ? self::DEFAULT_MIN_BOUNCES : $CFG->minbounces;
7173
}
7274

7375
/**
@@ -76,7 +78,39 @@ public static function get_min_bounces(): int {
7678
*/
7779
public static function get_bounce_ratio(): float {
7880
global $CFG;
79-
return $CFG->bounceratio ?? self::DEFAULT_BOUNCE_RATIO;
81+
return empty($CFG->bounceratio) ? self::DEFAULT_BOUNCE_RATIO : $CFG->bounceratio;
82+
}
83+
84+
/**
85+
* Does the config imply the use of consecutive bounce count?
86+
* @return bool use consecutive bounce count?
87+
*/
88+
public static function use_consecutive_bounces(): bool {
89+
// Using consecutive bounce count requires a negative bounce ratio to pass threshold checks.
90+
if (self::get_bounce_ratio() >= 0) {
91+
return false;
92+
}
93+
return true;
94+
}
95+
96+
97+
/**
98+
* Resets the bounce count of a user and fires off an event.
99+
* @param \stdClass $resetuser user who will have their bounce count reset
100+
* @return void
101+
*/
102+
public static function reset_bounce_count(\stdClass $resetuser): void {
103+
global $USER;
104+
105+
// Swap to set_bounce_count($resetuser, true) once MDL-73798 is integrated.
106+
unset_user_preference('email_bounce_count', $resetuser);
107+
108+
$event = bounce_count_reset::create([
109+
'userid' => $USER->id,
110+
'relateduserid' => $resetuser->id,
111+
'context' => \context_system::instance(),
112+
]);
113+
$event->trigger();
80114
}
81115

82116
/**

classes/sns_client.php

+14-8
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ class sns_client {
6464
/** Bounce */
6565
const BOUNCE_TYPE = 'Bounce';
6666

67+
/** Delivery */
68+
const DELIVERY_TYPE = 'Delivery';
69+
6770
/**
6871
* SNS Message Object
6972
* @var \Aws\Sns\Message
@@ -182,25 +185,28 @@ public function is_authorised() {
182185
* Validates the incoming message and sets up the sns_notification message.
183186
* This accepts message types of 'Notification'.
184187
*
188+
* @param Message|null $message message injector for unit testing
185189
* @return bool Successful validtion
186190
*/
187-
public function process_message() {
191+
public function process_message(?Message $message = null): bool {
188192
$this->validator = new MessageValidator();
189193
$this->client = new Client();
190194
$this->notification = new sns_notification();
191195

192196
// Get the message from the POST data.
193-
$this->message = Message::fromRawPostData();
197+
$this->message = PHPUNIT_TEST && isset($message) ? $message : Message::fromRawPostData();
194198

195199
$isvalidmessage = false;
196200

197201
// Validate the incoming message.
198-
try {
199-
$this->validator->validate($this->message);
200-
} catch (InvalidSnsMessageException $e) {
201-
// Message not valid!
202-
http_response_code(400); // Bad request.
203-
return $isvalidmessage;
202+
if (!PHPUNIT_TEST) {
203+
try {
204+
$this->validator->validate($this->message);
205+
} catch (InvalidSnsMessageException $e) {
206+
// Message not valid!
207+
http_response_code(400); // Bad request.
208+
return $isvalidmessage;
209+
}
204210
}
205211

206212
// Process the message depending on its type.

0 commit comments

Comments
 (0)