Skip to content

Commit

Permalink
Add more specific bounce handling
Browse files Browse the repository at this point in the history
  • Loading branch information
bwalkerl committed Nov 26, 2024
1 parent 8475b2e commit 2d79507
Show file tree
Hide file tree
Showing 12 changed files with 590 additions and 52 deletions.
3 changes: 1 addition & 2 deletions classes/check/bounces.php
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,7 @@ public function get_result() : result {
FROM {user_preferences} up
LEFT JOIN {user_preferences} up2 ON up2.name = 'email_send_count' AND up.userid = up2.userid
WHERE up.name = 'email_bounce_count' AND CAST(up.value AS INTEGER) > :threshold";
// Start with a threshold of 1 as that is the default for manually created users.
// TODO: Update when core is fixed.
// Start with a threshold of 1 as that was a historical default for manually created users.
$bounces = $DB->get_records_sql($sql, ['threshold' => 1]);
$userswithbounces = count($bounces);

Expand Down
58 changes: 58 additions & 0 deletions classes/event/bounce_count_reset.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Bounce count reset event
*
* @package tool_emailutils
* @author Benjamin Walker ([email protected])
* @copyright 2024 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_emailutils\event;

/**
* Event
*/
class bounce_count_reset extends \core\event\base {

/**
* Initialise required event data properties.
*/
protected function init(): void {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_OTHER;
}

/**
* Returns localised event name.
*
* @return string
*/
public static function get_name(): string {
return get_string('event:bouncecountreset', 'tool_emailutils');
}

/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description(): string {
$reason = empty($this->userid) ? 'because an email was delivered successfully' : 'manually';
return "The user with id '$this->relateduserid' had their email bounce count reset $reason.";
}
}
9 changes: 3 additions & 6 deletions classes/event/notification_received.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@
*/
namespace tool_emailutils\event;

use tool_emailutils;

/**
* Event
*/
Expand All @@ -34,18 +32,17 @@ class notification_received extends \core\event\base {
/**
* Initialise required event data properties.
*/
protected function init() {
protected function init(): void {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_OTHER;
$this->data['objecttable'] = 'user';
}

/**
* Returns localised event name.
*
* @return string
*/
public static function get_name() {
public static function get_name(): string {
return get_string('event:notificationreceived', 'tool_emailutils');
}

Expand All @@ -54,7 +51,7 @@ public static function get_name() {
*
* @return string
*/
public function get_description() {
public function get_description(): mixed {
return $this->other;
}
}
57 changes: 57 additions & 0 deletions classes/event/over_bounce_threshold.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.

/**
* Over bounce threshold event
*
* @package tool_emailutils
* @author Benjamin Walker ([email protected])
* @copyright 2024 Catalyst IT
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_emailutils\event;

/**
* Event
*/
class over_bounce_threshold extends \core\event\base {

/**
* Initialise required event data properties.
*/
protected function init(): void {
$this->data['crud'] = 'u';
$this->data['edulevel'] = self::LEVEL_OTHER;
}

/**
* Returns localised event name.
*
* @return string
*/
public static function get_name(): string {
return get_string('event:overbouncethreshold', 'tool_emailutils');
}

/**
* Returns non-localised description of what happened.
*
* @return string
*/
public function get_description(): string {
return "The user with id '$this->relateduserid' is now over the bounce threshold.";
}
}
38 changes: 36 additions & 2 deletions classes/helper.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@

namespace tool_emailutils;

use tool_emailutils\event\bounce_count_reset;

/**
* Helper class for tool_emailutils.
*
Expand Down Expand Up @@ -67,7 +69,7 @@ public static function get_username_fields(string $tablealias = ''): string {
*/
public static function get_min_bounces(): int {
global $CFG;
return $CFG->minbounce ?? self::DEFAULT_MIN_BOUNCES;
return empty($CFG->minbounces) ? self::DEFAULT_MIN_BOUNCES : $CFG->minbounces;
}

/**
Expand All @@ -76,7 +78,39 @@ public static function get_min_bounces(): int {
*/
public static function get_bounce_ratio(): float {
global $CFG;
return $CFG->bounceratio ?? self::DEFAULT_BOUNCE_RATIO;
return empty($CFG->bounceratio) ? self::DEFAULT_BOUNCE_RATIO : $CFG->bounceratio;
}

/**
* Does the config imply the use of consecutive bounce count?
* @return bool use consecutive bounce count?
*/
public static function use_consecutive_bounces(): bool {
// Using consecutive bounce count requires a negative bounce ratio to pass threshold checks.
if (self::get_bounce_ratio() >= 0) {
return false;
}
return true;
}


/**
* Resets the bounce count of a user and fires off an event.
* @param \stdClass $resetuser user who will have their bounce count reset
* @return void
*/
public static function reset_bounce_count(\stdClass $resetuser): void {
global $USER;

// Swap to set_bounce_count($resetuser, true) once MDL-73798 is integrated.
unset_user_preference('email_bounce_count', $resetuser);

$event = bounce_count_reset::create([
'userid' => $USER->id,
'relateduserid' => $resetuser->id,
'context' => \context_system::instance(),
]);
$event->trigger();
}

/**
Expand Down
22 changes: 14 additions & 8 deletions classes/sns_client.php
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ class sns_client {
/** Bounce */
const BOUNCE_TYPE = 'Bounce';

/** Delivery */
const DELIVERY_TYPE = 'Delivery';

/**
* SNS Message Object
* @var \Aws\Sns\Message
Expand Down Expand Up @@ -182,25 +185,28 @@ public function is_authorised() {
* Validates the incoming message and sets up the sns_notification message.
* This accepts message types of 'Notification'.
*
* @param Message|null $message message injector for unit testing
* @return bool Successful validtion
*/
public function process_message() {
public function process_message(?Message $message = null): bool {
$this->validator = new MessageValidator();
$this->client = new Client();
$this->notification = new sns_notification();

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

$isvalidmessage = false;

// Validate the incoming message.
try {
$this->validator->validate($this->message);
} catch (InvalidSnsMessageException $e) {
// Message not valid!
http_response_code(400); // Bad request.
return $isvalidmessage;
if (!PHPUNIT_TEST) {
try {
$this->validator->validate($this->message);
} catch (InvalidSnsMessageException $e) {
// Message not valid!
http_response_code(400); // Bad request.
return $isvalidmessage;
}
}

// Process the message depending on its type.
Expand Down
Loading

0 comments on commit 2d79507

Please sign in to comment.