From: Tim Düsterhus Date: Mon, 22 Jun 2020 13:43:31 +0000 (+0200) Subject: Add NotificationEmailDeliveryBackgroundJob X-Git-Tag: 5.3.0_Alpha_1~206^2^2~1 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=7fd5cf5411b1788aa963e1d06e0996a833909628;p=GitHub%2FWoltLab%2FWCF.git Add NotificationEmailDeliveryBackgroundJob --- diff --git a/wcfsetup/install/files/lib/system/background/job/NotificationEmailDeliveryBackgroundJob.class.php b/wcfsetup/install/files/lib/system/background/job/NotificationEmailDeliveryBackgroundJob.class.php new file mode 100644 index 0000000000..a908942dde --- /dev/null +++ b/wcfsetup/install/files/lib/system/background/job/NotificationEmailDeliveryBackgroundJob.class.php @@ -0,0 +1,132 @@ + + * @package WoltLabSuite\Core\System\Background\Job + * @since 5.3 + */ +class NotificationEmailDeliveryBackgroundJob extends AbstractBackgroundJob { + const MAX_FAILURES = EmailDeliveryBackgroundJob::MAX_FAILURES; + + /** + * @var EmailDeliveryBackgroundJob + */ + private $job; + + /** + * @var int + */ + private $notificationID; + + /** + * Wraps the given EmailDeliveryBackgroundJob and associates it with the given notification. + * + * The recipient is technically redundant, because the information can be retrieved using $notification->userID, + * the value is used as a safety check within the constructor to make sure all the checks run against the expected + * user. + * + * @param EmailDeliveryBackgroundJob $job + * @param User $recipient + * @param UserNotification $notification + */ + public function __construct(EmailDeliveryBackgroundJob $job, UserNotification $notification, User $recipient) { + $this->job = $job; + $this->notificationID = $notification->notificationID; + + if ($notification->userID != $recipient->userID) { + throw new \InvalidArgumentException("Mismatching userIDs within notification (".$notification->userID.") and recipient (".$recipient->userID.")."); + } + } + + /** + * Pass the failure along to the inner job to benefit from the retryAfter() logic. + */ + public function onFailure() { + $this->job->fail(); + } + + /** + * Inherit the retryAfter logic of the inner job. + */ + public function retryAfter() { + return $this->job->retryAfter(); + } + + /** + * @inheritDoc + */ + public function perform() { + // see UserNotificationHandler::fetchNotifications() + $sql = "SELECT notification.*, notification_event.eventID, object_type.objectType + FROM wcf".WCF_N."_user_notification notification + LEFT JOIN wcf".WCF_N."_user_notification_event notification_event + ON (notification_event.eventID = notification.eventID) + LEFT JOIN wcf".WCF_N."_object_type object_type + ON (object_type.objectTypeID = notification_event.objectTypeID) + WHERE notification.notificationID = ? + ORDER BY notification.time DESC"; + $statement = WCF::getDB()->prepareStatement($sql, 1); + $statement->execute([$this->notificationID]); + + /** @var UserNotification $notification */ + $notification = $statement->fetchObject(UserNotification::class); + $statement->closeCursor(); + + // Drop email if the notification is deleted. + if (!$notification->notificationID) { + return; + } + + $user = WCF::getUser(); + try { + // Switch user, because processNotifications() checks the permissions as the current user. + SessionHandler::getInstance()->changeUser(new User($notification->userID), true); + + $processedNotifications = UserNotificationHandler::getInstance()->processNotifications([$notification]); + + // Drop email if the processing dropped the notification (most likely due to a lack of permissions). + if ($processedNotifications['count'] == 0) { + return; + } + + // If no notification was dropped we expect to get back exactly one notification ... + if ($processedNotifications['count'] != 1) { + throw new \LogicException("Unreachable"); + } + + $processedNotification = $processedNotifications['notifications'][0]; + + // ... and we expect that this one notification is the one we passed in. + if ($processedNotification['notificationID'] != $notification->notificationID) { + throw new \LogicException("Unreachable"); + } + + /** @var IUserNotificationEvent $event */ + $event = $processedNotification['event']; + + // Drop email if the notification is already confirmed. + if ($event->isConfirmed()) { + return; + } + } + finally { + SessionHandler::getInstance()->changeUser($user, true); + } + + // If none of the checks failed we can send the notification after we switched + // back to the regular user (guest within the context of the queue). + $this->job->perform(); + } +}