Clean up mailing code
This commit is contained in:
parent
ca30d9d42c
commit
f67873467e
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "fwdekker/death-notifier",
|
||||
"description": "Get notified when a famous person dies.",
|
||||
"version": "0.15.4", "_comment_version": "Also update version in `package.json`!",
|
||||
"version": "0.15.5", "_comment_version": "Also update version in `package.json`!",
|
||||
"type": "project",
|
||||
"license": "MIT",
|
||||
"homepage": "https://git.fwdekker.com/tools/death-notifier",
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "death-notifier",
|
||||
"version": "0.15.4", "_comment_version": "Also update version in `composer.json`!",
|
||||
"version": "0.15.5", "_comment_version": "Also update version in `composer.json`!",
|
||||
"description": "Get notified when a famous person dies.",
|
||||
"author": "Florine W. Dekker",
|
||||
"browser": "dist/bundle.js",
|
||||
|
|
|
@ -4,7 +4,8 @@ use com\fwdekker\deathnotifier\ActionDispatcher;
|
|||
use com\fwdekker\deathnotifier\ActionMethod;
|
||||
use com\fwdekker\deathnotifier\Database;
|
||||
use com\fwdekker\deathnotifier\EmulateCronCliAction;
|
||||
use com\fwdekker\deathnotifier\mailer\Mailer;
|
||||
use com\fwdekker\deathnotifier\LoggerUtil;
|
||||
use com\fwdekker\deathnotifier\mailer\MailManager;
|
||||
use com\fwdekker\deathnotifier\mailer\ProcessEmailQueueCliAction;
|
||||
use com\fwdekker\deathnotifier\mediawiki\MediaWiki;
|
||||
use com\fwdekker\deathnotifier\Response;
|
||||
|
@ -36,14 +37,16 @@ require_once __DIR__ . "/.vendor/autoload.php";
|
|||
|
||||
// Preamble
|
||||
$config = Util::read_config();
|
||||
// TODO: Improve logging specificity and usefulness
|
||||
$logger = Util::create_logger($config["logger"]);
|
||||
$db = new Database($logger->withName("Database"), $config["database"]["filename"]);
|
||||
|
||||
$mailer = new Mailer($config, $logger->withName("Mailer"), $db->conn);
|
||||
$mediawiki = new MediaWiki($logger->withName("Mediawiki"));
|
||||
$user_manager = new UserManager($logger->withName("UserManager"), $db->conn, $mailer);
|
||||
$tracking_manager = new TrackingManager($logger->withName("TrackingManager"), $db->conn);
|
||||
LoggerUtil::configure($config["logger"]);
|
||||
$logger = LoggerUtil::with_name();
|
||||
|
||||
$db = new Database($config["database"]["filename"]);
|
||||
|
||||
$mediawiki = new MediaWiki();
|
||||
$mail_manager = new MailManager($db->conn);
|
||||
$user_manager = new UserManager($db->conn, $mail_manager);
|
||||
$tracking_manager = new TrackingManager($db->conn);
|
||||
|
||||
|
||||
// Handle request
|
||||
|
@ -53,7 +56,7 @@ try {
|
|||
$_POST = Util::parse_post();
|
||||
|
||||
// Update database
|
||||
$db->auto_install($mailer, $user_manager, $tracking_manager);
|
||||
$db->auto_install($mail_manager, $user_manager, $tracking_manager);
|
||||
$db->auto_migrate();
|
||||
|
||||
// Dispatch request
|
||||
|
@ -79,8 +82,8 @@ try {
|
|||
$dispatcher->register_action(new RemoveTrackingAction($tracking_manager));
|
||||
// CLI actions
|
||||
$cli_actions = [
|
||||
new UpdateTrackingsCliAction($config, $db->conn, $tracking_manager, $mediawiki, $mailer),
|
||||
new ProcessEmailQueueCliAction($config, $mailer),
|
||||
new UpdateTrackingsCliAction($config, $db->conn, $tracking_manager, $mediawiki, $mail_manager),
|
||||
new ProcessEmailQueueCliAction($config, $mail_manager),
|
||||
];
|
||||
$dispatcher->register_action($cli_actions[0]);
|
||||
$dispatcher->register_action($cli_actions[1]);
|
||||
|
|
|
@ -11,15 +11,15 @@ use com\fwdekker\deathnotifier\validator\Rule;
|
|||
abstract class CliAction extends Action
|
||||
{
|
||||
/**
|
||||
* @var mixed the application's configuration
|
||||
* @var array<string, mixed> the application's configuration
|
||||
*/
|
||||
private mixed $config;
|
||||
protected mixed $config;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new CLI action.
|
||||
*
|
||||
* @param mixed $config the application's configuration
|
||||
* @param array<string, mixed> $config the application's configuration
|
||||
* @param string $name the name of the action that this action handles
|
||||
* @param array<string, Rule[]> $rule_lists maps input keys to {@see Rule}s that should be validated before this
|
||||
* action is handled
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier;
|
||||
|
||||
use com\fwdekker\deathnotifier\mailer\Mailer;
|
||||
use com\fwdekker\deathnotifier\mailer\MailManager;
|
||||
use com\fwdekker\deathnotifier\tracking\TrackingManager;
|
||||
use com\fwdekker\deathnotifier\user\UserManager;
|
||||
use Composer\Semver\Comparator;
|
||||
|
@ -33,12 +33,11 @@ class Database
|
|||
/**
|
||||
* Opens a connection with the database at `filename`.
|
||||
*
|
||||
* @param Logger $logger the logger to use for logging
|
||||
* @param string $filename the path to the database to connect to
|
||||
*/
|
||||
public function __construct(Logger $logger, string $filename)
|
||||
public function __construct(string $filename)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->logger = LoggerUtil::with_name("Database");
|
||||
|
||||
$this->conn = new PDO("sqlite:$filename", options: array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
|
||||
$this->conn->exec("PRAGMA foreign_keys = ON;");
|
||||
|
@ -48,12 +47,12 @@ class Database
|
|||
/**
|
||||
* Installs all necessary data structures to get the database working.
|
||||
*
|
||||
* @param Mailer $mailer the mailer to install
|
||||
* @param MailManager $mailer the mailer to install
|
||||
* @param UserManager $userManager the use manager to install
|
||||
* @param TrackingManager $trackingManager the tracking manager to install
|
||||
* @return void
|
||||
*/
|
||||
public function auto_install(Mailer $mailer, UserManager $userManager, TrackingManager $trackingManager): void
|
||||
public function auto_install(MailManager $mailer, UserManager $userManager, TrackingManager $trackingManager): void
|
||||
{
|
||||
// TODO: Keep track of statistics, such as #users, mails sent, etc.
|
||||
self::transaction($this->conn, function () use ($mailer, $userManager, $trackingManager) {
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace com\fwdekker\deathnotifier;
|
||||
|
||||
use Error;
|
||||
|
||||
|
||||
/**
|
||||
* Thrown if something happens that should not be able to happen, and there is no way to recover.
|
||||
*/
|
||||
class InvalidStateException extends Error
|
||||
class IllegalStateException extends Error
|
||||
{
|
||||
// Intentionally left empty
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace com\fwdekker\deathnotifier;
|
||||
|
||||
use Monolog\ErrorHandler;
|
||||
use Monolog\Handler\StreamHandler;
|
||||
use Monolog\Logger;
|
||||
|
||||
|
||||
/**
|
||||
* Helps with creating loggers.
|
||||
*
|
||||
* Contains global state, but reduces unnecessary parameter passing and unused fields.
|
||||
*/
|
||||
// TODO: Improve logging specificity and usefulness
|
||||
class LoggerUtil
|
||||
{
|
||||
/**
|
||||
* @var Logger|null the main logger instance that other loggers are derived from
|
||||
*/
|
||||
private static ?Logger $main_logger = null;
|
||||
|
||||
|
||||
/**
|
||||
* Configures the main logger.
|
||||
*
|
||||
* @param array $config the logger configuration
|
||||
* @return void
|
||||
*/
|
||||
public static function configure(array $config): void
|
||||
{
|
||||
if (self::$main_logger !== null)
|
||||
throw new IllegalStateException("Logger has already been created.");
|
||||
|
||||
self::$main_logger = new Logger("main");
|
||||
self::$main_logger->pushHandler(new StreamHandler($config["file"], $config["level"]));
|
||||
ErrorHandler::register(self::$main_logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a logger with the given name.
|
||||
*
|
||||
* @param string $name the name of the logger to return
|
||||
* @return Logger a logger with the given name
|
||||
*/
|
||||
public static function with_name(string $name = "main"): Logger
|
||||
{
|
||||
if (self::$main_logger === null)
|
||||
throw new IllegalStateException("Logger has not been created yet.");
|
||||
|
||||
return self::$main_logger->withName($name);
|
||||
}
|
||||
}
|
|
@ -48,21 +48,6 @@ class Util
|
|||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the main logger instance.
|
||||
*
|
||||
* @param array<string, mixed> $logger_config the logger section of the configuration
|
||||
* @return Logger the main logger instance
|
||||
*/
|
||||
static function create_logger(array $logger_config): Logger
|
||||
{
|
||||
$logger = new Logger("main");
|
||||
$logger->pushHandler(new StreamHandler($logger_config["file"], $logger_config["level"]));
|
||||
ErrorHandler::register($logger);
|
||||
|
||||
return $logger;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses POST values from JSON-based inputs.
|
||||
*
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\mailer;
|
||||
|
||||
use com\fwdekker\deathnotifier\IllegalStateException;
|
||||
use com\fwdekker\deathnotifier\tracking\NotifyArticleDeletedEmail;
|
||||
use com\fwdekker\deathnotifier\tracking\NotifyArticleUndeletedEmail;
|
||||
use com\fwdekker\deathnotifier\tracking\NotifyStatusChangedEmail;
|
||||
|
@ -10,7 +11,6 @@ use com\fwdekker\deathnotifier\user\ChangedPasswordEmail;
|
|||
use com\fwdekker\deathnotifier\user\RegisterEmail;
|
||||
use com\fwdekker\deathnotifier\user\ResetPasswordEmail;
|
||||
use com\fwdekker\deathnotifier\user\VerifyEmailEmail;
|
||||
use Exception;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -65,7 +65,6 @@ abstract class Email
|
|||
* @param string $arg1 the first argument to construct the email
|
||||
* @param string $arg2 the second argument to construct the email
|
||||
* @return Email a deserialized email
|
||||
* @throws Exception if the `type` is not recognized
|
||||
*/
|
||||
public static function deserialize(string $type, string $recipient, string $arg1, string $arg2): Email
|
||||
{
|
||||
|
@ -80,7 +79,7 @@ abstract class Email
|
|||
NotifyArticleDeletedEmail::TYPE => new NotifyArticleDeletedEmail($recipient, $arg1),
|
||||
NotifyArticleUndeletedEmail::TYPE => new NotifyArticleUndeletedEmail($recipient, $arg1),
|
||||
NotifyStatusChangedEmail::TYPE => new NotifyStatusChangedEmail($recipient, $arg1, $arg2),
|
||||
default => throw new Exception("Unknown email type $type."),
|
||||
default => throw new IllegalStateException("Unknown email type $type."),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,102 @@
|
|||
<?php
|
||||
|
||||
namespace com\fwdekker\deathnotifier\mailer;
|
||||
|
||||
use PDO;
|
||||
|
||||
|
||||
/**
|
||||
* Queues up mails and sends them when appropriate.
|
||||
*/
|
||||
class MailManager
|
||||
{
|
||||
/**
|
||||
* @var PDO the database connection to interact with
|
||||
*/
|
||||
private PDO $conn;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new mailer.
|
||||
*
|
||||
* @param PDO $conn the connection to the email database
|
||||
*/
|
||||
public function __construct(PDO $conn)
|
||||
{
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Populates the database with the necessary structures for emails.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function install(): void
|
||||
{
|
||||
$this->conn->exec("CREATE TABLE email_tasks(type TEXT NOT NULL,
|
||||
recipient TEXT NOT NULL,
|
||||
arg1 TEXT NOT NULL DEFAULT(''),
|
||||
arg2 TEXT NOT NULL DEFAULT(''),
|
||||
PRIMARY KEY (type, recipient, arg1, arg2));");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queues an email to be sent.
|
||||
*
|
||||
* @param Email $email the email to queue
|
||||
* @return void
|
||||
*/
|
||||
public function queue_email(Email $email): void
|
||||
{
|
||||
$stmt = $this->conn->prepare("INSERT OR IGNORE INTO email_tasks (type, recipient, arg1, arg2)
|
||||
VALUES (:type, :recipient, :arg1, :arg2);");
|
||||
$stmt->bindValue(":type", $email->type);
|
||||
$stmt->bindValue(":recipient", $email->recipient);
|
||||
$stmt->bindValue(":arg1", $email->arg1);
|
||||
$stmt->bindValue(":arg2", $email->arg2);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all queued emails.
|
||||
*
|
||||
* @return array<Email> the queued emails
|
||||
*/
|
||||
public function get_queue(): array
|
||||
{
|
||||
$stmt = $this->conn->prepare("SELECT type, recipient, arg1, arg2 FROM email_tasks;");
|
||||
$stmt->execute();
|
||||
$emails = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return array_map(
|
||||
fn($row) => Email::deserialize($row["type"], $row["recipient"], $row["arg1"], $row["arg2"]),
|
||||
$emails
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes mails from the queue.
|
||||
*
|
||||
* @param array<Email> $emails the emails to remove from the queue
|
||||
* @return void
|
||||
*/
|
||||
public function unqueue_emails(array $emails): void
|
||||
{
|
||||
$stmt = $this->conn->prepare("DELETE FROM email_tasks
|
||||
WHERE type=:type AND recipient=:recipient AND arg1=:arg1 AND arg2=:arg2;");
|
||||
$stmt->bindParam(":type", $type);
|
||||
$stmt->bindParam(":recipient", $recipient);
|
||||
$stmt->bindParam(":arg1", $arg1);
|
||||
$stmt->bindParam(":arg2", $arg2);
|
||||
|
||||
foreach ($emails as $email) {
|
||||
$type = $email->type;
|
||||
$recipient = $email->recipient;
|
||||
$arg1 = $email->arg1;
|
||||
$arg2 = $email->arg2;
|
||||
|
||||
$stmt->execute();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,149 +0,0 @@
|
|||
<?php
|
||||
|
||||
namespace com\fwdekker\deathnotifier\mailer;
|
||||
|
||||
use com\fwdekker\deathnotifier\Response;
|
||||
use Exception;
|
||||
use Monolog\Logger;
|
||||
use PDO;
|
||||
use PHPMailer\PHPMailer\Exception as MailerException;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\SMTP;
|
||||
|
||||
|
||||
/**
|
||||
* Queues up mails and sends them when appropriate.
|
||||
*/
|
||||
class Mailer
|
||||
{
|
||||
/**
|
||||
* @var Logger the logger to use for logging
|
||||
*/
|
||||
private Logger $logger;
|
||||
/**
|
||||
* @var array<string, array<string, mixed>> the configuration to use for mailing
|
||||
*/
|
||||
private array $config;
|
||||
/**
|
||||
* @var PDO the database connection to interact with
|
||||
*/
|
||||
private PDO $conn;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new mailer.
|
||||
*
|
||||
* @param array<string, array<string, mixed>> $config the configuration to use for mailing
|
||||
* @param Logger $logger the logger to use for logging
|
||||
* @param PDO $conn the connection to the email database
|
||||
*/
|
||||
public function __construct(array $config, Logger $logger, PDO $conn)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->config = $config;
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Populates the database with the necessary structures for emails.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function install(): void
|
||||
{
|
||||
$this->conn->exec("CREATE TABLE email_tasks(type TEXT NOT NULL,
|
||||
recipient TEXT NOT NULL,
|
||||
arg1 TEXT NOT NULL DEFAULT(''),
|
||||
arg2 TEXT NOT NULL DEFAULT(''),
|
||||
PRIMARY KEY (type, recipient, arg1, arg2));");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Queues an email to be sent.
|
||||
*
|
||||
* @param Email $email the email to queue
|
||||
* @return Response a satisfied `Response` if the email was queued, or an unsatisfied `Response` otherwise
|
||||
*/
|
||||
public function queue_email(Email $email): Response
|
||||
{
|
||||
$stmt = $this->conn->prepare("INSERT OR IGNORE INTO email_tasks (type, recipient, arg1, arg2)
|
||||
VALUES (:type, :recipient, :arg1, :arg2);");
|
||||
$stmt->bindValue(":type", $email->type);
|
||||
$stmt->bindValue(":recipient", $email->recipient);
|
||||
$stmt->bindValue(":arg1", $email->arg1);
|
||||
$stmt->bindValue(":arg2", $email->arg2);
|
||||
|
||||
return $stmt->execute() ? Response::satisfied() : Response::unsatisfied(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends all emails in the queue.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function process_queue(): void
|
||||
{
|
||||
// Open mailer
|
||||
$mailer = new PHPMailer();
|
||||
$mailer->IsSMTP();
|
||||
$mailer->CharSet = "UTF-8";
|
||||
|
||||
$mailer->SMTPAuth = true;
|
||||
$mailer->SMTPDebug = SMTP::DEBUG_OFF;
|
||||
$mailer->SMTPKeepAlive = true;
|
||||
$mailer->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
|
||||
$mailer->Host = $this->config["mail"]["host"];
|
||||
$mailer->Port = $this->config["mail"]["port"];
|
||||
$mailer->Username = $this->config["mail"]["username"];
|
||||
$mailer->Password = $this->config["mail"]["password"];
|
||||
try {
|
||||
$mailer->setFrom($this->config["mail"]["username"], $this->config["mail"]["from_name"]);
|
||||
} catch (MailerException $exception) {
|
||||
$this->logger->error("Failed to set 'from' address while processing queue.", ["cause" => $exception]);
|
||||
$mailer->smtpClose();
|
||||
}
|
||||
|
||||
// Get queue
|
||||
$stmt = $this->conn->prepare("SELECT type, recipient, arg1, arg2 FROM email_tasks;");
|
||||
$stmt->execute();
|
||||
$email_tasks = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Process queue
|
||||
$stmt = $this->conn->prepare("DELETE FROM email_tasks
|
||||
WHERE type=:type AND recipient=:recipient AND arg1=:arg1 AND arg2=:arg2;");
|
||||
$stmt->bindParam(":type", $type);
|
||||
$stmt->bindParam(":recipient", $recipient);
|
||||
$stmt->bindParam(":arg1", $arg1);
|
||||
$stmt->bindParam(":arg2", $arg2);
|
||||
foreach ($email_tasks as ["type" => $type, "recipient" => $recipient, "arg1" => $arg1, "arg2" => $arg2]) {
|
||||
try {
|
||||
$email = Email::deserialize($type, $recipient, $arg1, $arg2);
|
||||
$mailer->Subject = $email->getSubject();
|
||||
$mailer->Body = $email->getBody($this->config);
|
||||
|
||||
try {
|
||||
$mailer->addAddress($recipient);
|
||||
$mailer->send();
|
||||
} catch (MailerException $exception) {
|
||||
$this->logger->error(
|
||||
"Failed to send mail.",
|
||||
["cause" => $exception, "email" => $email]
|
||||
);
|
||||
$mailer->getSMTPInstance()->reset();
|
||||
}
|
||||
|
||||
$mailer->clearAddresses();
|
||||
} catch (Exception $exception) {
|
||||
$this->logger->error(
|
||||
"Failed to send mail.",
|
||||
["cause" => $exception]
|
||||
);
|
||||
$mailer->getSMTPInstance()->reset();
|
||||
}
|
||||
|
||||
$stmt->execute();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,7 +2,13 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\mailer;
|
||||
|
||||
use com\fwdekker\deathnotifier\ActionException;
|
||||
use com\fwdekker\deathnotifier\CliAction;
|
||||
use com\fwdekker\deathnotifier\LoggerUtil;
|
||||
use Monolog\Logger;
|
||||
use PHPMailer\PHPMailer\Exception as PHPMailerException;
|
||||
use PHPMailer\PHPMailer\PHPMailer;
|
||||
use PHPMailer\PHPMailer\SMTP;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -11,22 +17,27 @@ use com\fwdekker\deathnotifier\CliAction;
|
|||
class ProcessEmailQueueCliAction extends CliAction
|
||||
{
|
||||
/**
|
||||
* @var Mailer the mailer to process the queue with
|
||||
* @var Logger the logger to log with
|
||||
*/
|
||||
private readonly Mailer $mailer;
|
||||
private readonly Logger $logger;
|
||||
/**
|
||||
* @var MailManager the manager to process the queue with
|
||||
*/
|
||||
private readonly MailManager $mail_manager;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new `ProcessEmailQueueAction`.
|
||||
*
|
||||
* @param mixed $config the application's configuration
|
||||
* @param Mailer $mailer the mailer to process the queue with
|
||||
* @param MailManager $mail_manager the manager to process the queue with
|
||||
*/
|
||||
public function __construct(mixed $config, Mailer $mailer)
|
||||
public function __construct(mixed $config, MailManager $mail_manager)
|
||||
{
|
||||
parent::__construct($config, "process-email-queue");
|
||||
|
||||
$this->mailer = $mailer;
|
||||
$this->logger = LoggerUtil::with_name("ProcessEmailQueueCliAction");
|
||||
$this->mail_manager = $mail_manager;
|
||||
}
|
||||
|
||||
|
||||
|
@ -34,11 +45,64 @@ class ProcessEmailQueueCliAction extends CliAction
|
|||
* Processes the queue.
|
||||
*
|
||||
* @return mixed `null`
|
||||
* @throws ActionException if the mailer could not be created or if an email could not be sent
|
||||
*/
|
||||
public function handle(): mixed
|
||||
{
|
||||
$this->mailer->process_queue();
|
||||
$emails = $this->mail_manager->get_queue();
|
||||
|
||||
$mailer = $this->create_mailer();
|
||||
foreach ($emails as $email) {
|
||||
$mailer->Subject = $email->getSubject();
|
||||
$mailer->Body = $email->getBody($this->config);
|
||||
|
||||
try {
|
||||
$mailer->addAddress($email->recipient);
|
||||
$mailer->send();
|
||||
} catch (PHPMailerException $exception) {
|
||||
$mailer->getSMTPInstance()->reset();
|
||||
|
||||
$this->logger->error("Failed to send email.", ["cause" => $exception]);
|
||||
throw new ActionException("Failed to send mail.");
|
||||
}
|
||||
|
||||
$mailer->clearAddresses();
|
||||
}
|
||||
|
||||
$this->mail_manager->unqueue_emails($emails);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a mailer object to send emails with.
|
||||
*
|
||||
* @throws ActionException if the mailer could not be created
|
||||
*/
|
||||
private function create_mailer(): PHPMailer
|
||||
{
|
||||
$mailer = new PHPMailer();
|
||||
$mailer->IsSMTP();
|
||||
$mailer->CharSet = "UTF-8";
|
||||
|
||||
$mailer->SMTPAuth = true;
|
||||
$mailer->SMTPDebug = SMTP::DEBUG_OFF;
|
||||
$mailer->SMTPKeepAlive = true;
|
||||
$mailer->SMTPSecure = PHPMailer::ENCRYPTION_SMTPS;
|
||||
$mailer->Host = $this->config["mail"]["host"];
|
||||
$mailer->Port = $this->config["mail"]["port"];
|
||||
$mailer->Username = $this->config["mail"]["username"];
|
||||
$mailer->Password = $this->config["mail"]["password"];
|
||||
try {
|
||||
$mailer->setFrom($this->config["mail"]["username"], $this->config["mail"]["from_name"]);
|
||||
} catch (PHPMailerException $exception) {
|
||||
$mailer->smtpClose();
|
||||
|
||||
$this->logger->error("Failed to set 'from' address while processing email queue.", ["cause" => $exception]);
|
||||
throw new ActionException("Failed to set 'from' address while processing queue.");
|
||||
}
|
||||
|
||||
return $mailer;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier\mediawiki;
|
||||
|
||||
use com\fwdekker\deathnotifier\LoggerUtil;
|
||||
use Monolog\Logger;
|
||||
|
||||
|
||||
|
@ -40,12 +41,10 @@ class MediaWiki
|
|||
|
||||
/**
|
||||
* Creates a new Mediawiki instance.
|
||||
*
|
||||
* @param Logger $logger the logger to use for logging
|
||||
*/
|
||||
public function __construct(Logger $logger)
|
||||
public function __construct()
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->logger = LoggerUtil::with_name("MediaWiki");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -5,16 +5,13 @@ namespace com\fwdekker\deathnotifier\tracking;
|
|||
use com\fwdekker\deathnotifier\Action;
|
||||
use com\fwdekker\deathnotifier\ActionException;
|
||||
use com\fwdekker\deathnotifier\ActionMethod;
|
||||
use com\fwdekker\deathnotifier\mediawiki\ArticleType;
|
||||
use com\fwdekker\deathnotifier\IllegalStateException;
|
||||
use com\fwdekker\deathnotifier\mediawiki\IsPersonPageRule;
|
||||
use com\fwdekker\deathnotifier\mediawiki\MediaWiki;
|
||||
use com\fwdekker\deathnotifier\mediawiki\MediaWikiException;
|
||||
use com\fwdekker\deathnotifier\Response;
|
||||
use com\fwdekker\deathnotifier\ValidationException;
|
||||
use com\fwdekker\deathnotifier\validator\HasLengthRule;
|
||||
use com\fwdekker\deathnotifier\validator\IsNotBlankRule;
|
||||
use Exception;
|
||||
use InvalidStateException;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -83,7 +80,7 @@ class AddTrackingAction extends Action
|
|||
$normalized_name = $info->redirects[$person_name];
|
||||
$status = $info->results[$normalized_name]["status"];
|
||||
if ($status === null)
|
||||
throw new InvalidStateException("Article is about person, but person has no status.");
|
||||
throw new IllegalStateException("Article is about person, but person has no status.");
|
||||
|
||||
// Add tracking (Transaction is not necessary)
|
||||
if ($this->tracking_manager->has_tracking($user_uuid, $normalized_name))
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
namespace com\fwdekker\deathnotifier\tracking;
|
||||
|
||||
use com\fwdekker\deathnotifier\Database;
|
||||
use com\fwdekker\deathnotifier\LoggerUtil;
|
||||
use com\fwdekker\deathnotifier\mediawiki\ArticleType;
|
||||
use com\fwdekker\deathnotifier\mediawiki\PersonStatus;
|
||||
use Monolog\Logger;
|
||||
|
@ -36,12 +37,11 @@ class TrackingManager
|
|||
/**
|
||||
* Constructs a new tracking manager.
|
||||
*
|
||||
* @param Logger $logger the logger to log with
|
||||
* @param PDO $conn the database connection to interact with
|
||||
*/
|
||||
public function __construct(Logger $logger, PDO $conn)
|
||||
public function __construct(PDO $conn)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->logger = LoggerUtil::with_name("TrackingManager");
|
||||
$this->conn = $conn;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ namespace com\fwdekker\deathnotifier\tracking;
|
|||
use com\fwdekker\deathnotifier\ActionException;
|
||||
use com\fwdekker\deathnotifier\CliAction;
|
||||
use com\fwdekker\deathnotifier\Database;
|
||||
use com\fwdekker\deathnotifier\mailer\Mailer;
|
||||
use com\fwdekker\deathnotifier\mailer\MailManager;
|
||||
use com\fwdekker\deathnotifier\mediawiki\MediaWiki;
|
||||
use com\fwdekker\deathnotifier\mediawiki\MediaWikiException;
|
||||
use PDO;
|
||||
|
@ -29,9 +29,9 @@ class UpdateTrackingsCliAction extends CliAction
|
|||
*/
|
||||
private readonly MediaWiki $mediawiki;
|
||||
/**
|
||||
* @var Mailer the mailer to send emails with
|
||||
* @var MailManager the mailer to send emails with
|
||||
*/
|
||||
private readonly Mailer $mailer;
|
||||
private readonly MailManager $mailer;
|
||||
|
||||
|
||||
/**
|
||||
|
@ -41,10 +41,10 @@ class UpdateTrackingsCliAction extends CliAction
|
|||
* @param PDO $conn the database connection to interact with
|
||||
* @param TrackingManager $tracking_manager the manager through which trackings should be updated
|
||||
* @param MediaWiki $mediawiki the instance to connect to Wikipedia with
|
||||
* @param Mailer $mailer the mailer to send emails with
|
||||
* @param MailManager $mailer the mailer to send emails with
|
||||
*/
|
||||
public function __construct(mixed $config, PDO $conn, TrackingManager $tracking_manager, MediaWiki $mediawiki,
|
||||
Mailer $mailer)
|
||||
public function __construct(mixed $config, PDO $conn, TrackingManager $tracking_manager, MediaWiki $mediawiki,
|
||||
MailManager $mailer)
|
||||
{
|
||||
parent::__construct($config, "update-trackings");
|
||||
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
namespace com\fwdekker\deathnotifier\user;
|
||||
|
||||
use com\fwdekker\deathnotifier\Database;
|
||||
use com\fwdekker\deathnotifier\mailer\Mailer;
|
||||
use com\fwdekker\deathnotifier\LoggerUtil;
|
||||
use com\fwdekker\deathnotifier\mailer\MailManager;
|
||||
use com\fwdekker\deathnotifier\Response;
|
||||
use Monolog\Logger;
|
||||
use PDO;
|
||||
|
@ -49,21 +50,20 @@ class UserManager
|
|||
*/
|
||||
private PDO $conn;
|
||||
/**
|
||||
* @var Mailer the mailer to send emails with
|
||||
* @var MailManager the mailer to send emails with
|
||||
*/
|
||||
private Mailer $mailer;
|
||||
private MailManager $mailer;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a new user manager.
|
||||
*
|
||||
* @param Logger $logger the logger to use for logging
|
||||
* @param PDO $conn the database connection to interact with
|
||||
* @param Mailer $mailer the mailer to send emails with
|
||||
* @param MailManager $mailer the mailer to send emails with
|
||||
*/
|
||||
public function __construct(Logger $logger, PDO $conn, Mailer $mailer)
|
||||
public function __construct(PDO $conn, MailManager $mailer)
|
||||
{
|
||||
$this->logger = $logger;
|
||||
$this->logger = LoggerUtil::with_name("UserManager");
|
||||
$this->conn = $conn;
|
||||
$this->mailer = $mailer;
|
||||
}
|
||||
|
|
|
@ -2,11 +2,10 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier;
|
||||
|
||||
use com\fwdekker\deathnotifier\mailer\Mailer;
|
||||
use com\fwdekker\deathnotifier\mailer\MailManager;
|
||||
use com\fwdekker\deathnotifier\tracking\TrackingManager;
|
||||
use com\fwdekker\deathnotifier\user\UserManager;
|
||||
use Exception;
|
||||
use Monolog\Logger;
|
||||
use Monolog\Test\TestCase;
|
||||
|
||||
|
||||
|
@ -23,10 +22,6 @@ abstract class DatabaseTestCase extends TestCase
|
|||
*/
|
||||
private static string $db_tmp_dir;
|
||||
|
||||
/**
|
||||
* @var Logger the logger
|
||||
*/
|
||||
public Logger $logger;
|
||||
/**
|
||||
* @var Database the database
|
||||
*/
|
||||
|
@ -34,11 +29,11 @@ abstract class DatabaseTestCase extends TestCase
|
|||
|
||||
|
||||
/**
|
||||
* @return Mailer the `Mailer` to install the database schema of
|
||||
* @return MailManager the `Mailer` to install the database schema of
|
||||
*/
|
||||
function get_mailer(): Mailer
|
||||
function get_mailer(): MailManager
|
||||
{
|
||||
return $this->createStub(Mailer::class);
|
||||
return $this->createStub(MailManager::class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -91,9 +86,7 @@ abstract class DatabaseTestCase extends TestCase
|
|||
$db_tmp_file = tempnam(self::$db_tmp_dir, "database-");
|
||||
if ($db_tmp_file === false) throw new Exception("Failed to create temporary database file.");
|
||||
|
||||
$this->logger = new Logger("DatabaseTestCase");
|
||||
$this->database = new Database($this->logger, $db_tmp_file);
|
||||
|
||||
$this->database = new Database($db_tmp_file);
|
||||
$this->database->auto_install($this->get_mailer(), $this->get_user_manager(), $this->get_tracking_manager());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
namespace com\fwdekker\deathnotifier;
|
||||
|
||||
use com\fwdekker\deathnotifier\mailer\Mailer;
|
||||
use com\fwdekker\deathnotifier\mailer\MailManager;
|
||||
use com\fwdekker\deathnotifier\user\ChangedEmailEmail;
|
||||
use com\fwdekker\deathnotifier\user\ChangedPasswordEmail;
|
||||
use com\fwdekker\deathnotifier\user\RegisterEmail;
|
||||
|
@ -19,7 +19,7 @@ use PHPUnit\Framework\MockObject\MockObject;
|
|||
class UserManagerTest extends DatabaseTestCase
|
||||
{
|
||||
private UserManager $user_manager;
|
||||
private Mailer&MockObject $mailer;
|
||||
private MailManager&MockObject $mailer;
|
||||
|
||||
|
||||
public function get_user_manager(): UserManager
|
||||
|
@ -29,7 +29,7 @@ class UserManagerTest extends DatabaseTestCase
|
|||
|
||||
public function setUp(): void
|
||||
{
|
||||
$this->mailer = $this->createMock(Mailer::class);
|
||||
$this->mailer = $this->createMock(MailManager::class);
|
||||
$this->mailer->method("queue_email")->willReturn(Response::satisfied());
|
||||
|
||||
parent::setUp();
|
||||
|
|
Loading…
Reference in New Issue