Added email notification on new private messages

This commit is contained in:
Igor Scheller 2023-02-26 16:18:16 +01:00 committed by Michael Weimann
parent a464682b47
commit fb8c05edad
17 changed files with 265 additions and 5 deletions

View File

@ -69,6 +69,9 @@ return [
// callable like [$instance, 'method'] or 'function'
// or $function
// ]
'message.created' => \Engelsystem\Events\Listener\Messages::class . '@created',
'news.created' => \Engelsystem\Events\Listener\News::class . '@created',
'oauth2.login' => \Engelsystem\Events\Listener\OAuth2::class . '@login',

View File

@ -18,6 +18,7 @@ class SettingsFactory extends Factory
'language' => $this->faker->locale(),
'theme' => $this->faker->numberBetween(1, 20),
'email_human' => $this->faker->boolean(),
'email_messages' => $this->faker->boolean(),
'email_goody' => $this->faker->boolean(),
'email_shiftinfo' => $this->faker->boolean(),
'email_news' => $this->faker->boolean(),

View File

@ -0,0 +1,33 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Migrations;
use Engelsystem\Database\Migration\Migration;
use Illuminate\Database\Schema\Blueprint;
class AddEmailMessagesToUsersSettings extends Migration
{
use Reference;
/**
* Run the migration
*/
public function up(): void
{
$this->schema->table('users_settings', function (Blueprint $table): void {
$table->boolean('email_messages')->default(false)->after('email_human');
});
}
/**
* Reverse the migration
*/
public function down(): void
{
$this->schema->table('users_settings', function (Blueprint $table): void {
$table->dropColumn('email_messages');
});
}
}

View File

@ -57,6 +57,7 @@ function guest_register()
$pronoun = '';
$email_shiftinfo = false;
$email_by_human_allowed = false;
$email_messages = false;
$email_news = false;
$email_goody = false;
$tshirt_size = '';
@ -155,6 +156,10 @@ function guest_register()
$email_by_human_allowed = true;
}
if ($request->has('email_messages')) {
$email_messages = true;
}
if ($request->has('email_news')) {
$email_news = true;
}
@ -263,6 +268,7 @@ function guest_register()
'language' => $session->get('locale'),
'theme' => config('theme'),
'email_human' => $email_by_human_allowed,
'email_messages' => $email_messages,
'email_goody' => $email_goody,
'email_shiftinfo' => $email_shiftinfo,
'email_news' => $email_news,
@ -434,6 +440,11 @@ function guest_register()
__('Notify me of new news'),
$email_news
),
form_checkbox(
'email_messages',
__('settings.profile.email_messages'),
$email_messages
),
form_checkbox(
'email_by_human_allowed',
__('Allow heaven angels to contact you by e-mail.'),

View File

@ -166,6 +166,12 @@ msgstr "Es gibt eine neue News: %1$s"
msgid "notification.news.new.text"
msgstr "Du kannst sie dir unter %3$s anschauen."
msgid "notification.messages.new"
msgstr "Neue private Nachricht von %s"
msgid "notification.messages.new.text"
msgstr "Du hast eine neue private Nachricht von %s bekommen. Du kannst sie dir unter %s anschauen."
msgid "notification.angeltype.confirmed"
msgstr "Du wurdest als %s bestätigt"

View File

@ -2999,6 +2999,9 @@ msgstr "Das %s darf mir E-Mails senden (z.B. wenn sich meine Schichten ändern).
msgid "settings.profile.email_news"
msgstr "Benachrichtige mich bei neuen News."
msgid "settings.profile.email_messages"
msgstr "Benachrichtige mich bei neuen privaten Nachrichten."
msgid "settings.profile.email_by_human_allowed"
msgstr "Erlaube Himmel-Engeln dich per Mail zu kontaktieren."

View File

@ -165,6 +165,12 @@ msgstr "A new news is available: %1$s"
msgid "notification.news.new.text"
msgstr "You can watch it at %3$s"
msgid "notification.messages.new"
msgstr "New private message from %s"
msgid "notification.messages.new.text"
msgstr "you've got a new private message from %s. You can view it under %s"
msgid "notification.angeltype.confirmed"
msgstr "You have been confirmed as an %s"

View File

@ -268,6 +268,9 @@ msgstr "The %s is allowed to send me an e-mail (e.g. when my shifts change)."
msgid "settings.profile.email_news"
msgstr "Notify me of new news."
msgid "settings.profile.email_messages"
msgstr "Notify me on new private messages."
msgid "settings.profile.email_by_human_allowed"
msgstr "Allow heaven angels to contact you by e-mail."

View File

@ -0,0 +1,8 @@
{% extends "emails/mail.twig" %}
{% block introduction %}
{% endblock %}
{% block message %}
{{ __('notification.messages.new.text', [sender, url('/messages/' ~ send_message.sender.id)]) }}
{% endblock %}

View File

@ -117,6 +117,11 @@
__('settings.profile.email_news'),
user.settings.email_news
) }}
{{ f.checkbox(
'email_messages',
__('settings.profile.email_messages'),
user.settings.email_messages
) }}
{{ f.checkbox(
'email_human',
__('settings.profile.email_by_human_allowed'),

View File

@ -149,6 +149,8 @@ class MessagesController extends BaseController
$newMessage->read = $otherUser->id == $currentUser->id; // if its to myself, I obviously read it.
$newMessage->save();
event('message.created', ['message' => $newMessage]);
return $this->redirect->to('/messages/' . $otherUser->id . '#newest');
}

View File

@ -85,6 +85,7 @@ class SettingsController extends BaseController
$user->settings->email_shiftinfo = $data['email_shiftinfo'] ?: false;
$user->settings->email_news = $data['email_news'] ?: false;
$user->settings->email_human = $data['email_human'] ?: false;
$user->settings->email_messages = $data['email_messages'] ?: false;
if (config('enable_goody')) {
$user->settings->email_goody = $data['email_goody'] ?: false;
@ -275,6 +276,7 @@ class SettingsController extends BaseController
'email_shiftinfo' => 'optional|checked',
'email_news' => 'optional|checked',
'email_human' => 'optional|checked',
'email_messages' => 'optional|checked',
'email_goody' => 'optional|checked',
];
if (config('enable_planned_arrival')) {

View File

@ -0,0 +1,46 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Events\Listener;
use Engelsystem\Mail\EngelsystemMailer;
use Engelsystem\Models\Message;
use Engelsystem\Models\User\User;
use Psr\Log\LoggerInterface;
use Symfony\Component\Mailer\Exception\TransportException;
class Messages
{
public function __construct(
protected LoggerInterface $log,
protected EngelsystemMailer $mailer
) {
}
public function created(Message $message): void
{
if (!$message->receiver->settings->email_messages) {
return;
}
$this->sendMail($message, $message->receiver, 'notification.messages.new', 'emails/messages-new');
}
private function sendMail(Message $message, User $user, string $subject, string $template): void
{
try {
$this->mailer->sendViewTranslated(
$user,
$subject,
$template,
['sender' => $message->sender->name, 'send_message' => $message, 'username' => $user->name]
);
} catch (TransportException $e) {
$this->log->error(
'Unable to send email "{title}" to user {user} with {exception}',
['title' => $subject, 'user' => $user->name, 'exception' => $e]
);
}
}
}

View File

@ -11,6 +11,7 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
* @property string $language
* @property int $theme
* @property bool $email_human
* @property bool $email_messages
* @property bool $email_goody
* @property bool $email_shiftinfo
* @property bool $email_news
@ -19,6 +20,7 @@ use Illuminate\Database\Query\Builder as QueryBuilder;
* @method static QueryBuilder|Settings[] whereLanguage($value)
* @method static QueryBuilder|Settings[] whereTheme($value)
* @method static QueryBuilder|Settings[] whereEmailHuman($value)
* @method static QueryBuilder|Settings[] whereEmailMessages($value)
* @method static QueryBuilder|Settings[] whereEmailGoody($value)
* @method static QueryBuilder|Settings[] whereEmailShiftinfo($value)
* @method static QueryBuilder|Settings[] whereEmailNews($value)
@ -34,6 +36,7 @@ class Settings extends HasUserModel
/** @var array<string, bool> Default attributes */
protected $attributes = [ // phpcs:ignore
'email_human' => false,
'email_messages' => false,
'email_goody' => false,
'email_shiftinfo' => false,
'email_news' => false,
@ -50,6 +53,7 @@ class Settings extends HasUserModel
'language',
'theme',
'email_human',
'email_messages',
'email_goody',
'email_shiftinfo',
'email_news',
@ -61,6 +65,7 @@ class Settings extends HasUserModel
'user_id' => 'integer',
'theme' => 'integer',
'email_human' => 'boolean',
'email_messages' => 'boolean',
'email_goody' => 'boolean',
'email_shiftinfo' => 'boolean',
'email_news' => 'boolean',

View File

@ -6,6 +6,7 @@ namespace Engelsystem\Test\Unit\Controllers;
use Carbon\Carbon;
use Engelsystem\Controllers\MessagesController;
use Engelsystem\Events\EventDispatcher;
use Engelsystem\Helpers\Authenticator;
use Engelsystem\Http\Exceptions\HttpForbidden;
use Engelsystem\Http\Exceptions\ValidationException;
@ -34,6 +35,8 @@ class MessagesControllerTest extends ControllerTest
protected Carbon $oneMinuteAgo;
protected Carbon $twoMinutesAgo;
protected EventDispatcher $events;
/**
* @testdox index: underNormalConditions -> returnsCorrectViewAndData
* @covers \Engelsystem\Controllers\MessagesController::__construct
@ -441,6 +444,8 @@ class MessagesControllerTest extends ControllerTest
->with('http://localhost/messages/' . $this->userB->id . '#newest')
->willReturn($this->response);
$this->setExpects($this->events, 'dispatch', ['message.created'], []);
$this->controller->send($this->request);
$msg = Message::whereText('a')->first();
@ -463,6 +468,8 @@ class MessagesControllerTest extends ControllerTest
->with('http://localhost/messages/' . $this->userA->id . '#newest')
->willReturn($this->response);
$this->setExpects($this->events, 'dispatch', ['message.created'], []);
$this->controller->send($this->request);
$msg = Message::whereText('a')->first();
@ -536,6 +543,9 @@ class MessagesControllerTest extends ControllerTest
$this->controller = $this->app->get(MessagesController::class);
$this->controller->setValidator(new Validator());
$this->events = $this->createMock(EventDispatcher::class);
$this->app->instance('events.dispatcher', $this->events);
}
protected function assertArrayOrCollection(mixed $obj): void

View File

@ -45,6 +45,7 @@ class SettingsControllerTest extends ControllerTest
'email_shiftinfo' => true,
'email_news' => true,
'email_human' => true,
'email_messages' => true,
'email_goody' => true,
'shirt_size' => 'S',
];
@ -122,6 +123,7 @@ class SettingsControllerTest extends ControllerTest
$this->assertEquals($body['email_shiftinfo'], $this->user->settings->email_shiftinfo);
$this->assertEquals($body['email_news'], $this->user->settings->email_news);
$this->assertEquals($body['email_human'], $this->user->settings->email_human);
$this->assertEquals($body['email_messages'], $this->user->settings->email_messages);
$this->assertEquals($body['email_goody'], $this->user->settings->email_goody);
$this->assertEquals($body['shirt_size'], $this->user->personalData->shirt_size);
}

View File

@ -0,0 +1,114 @@
<?php
declare(strict_types=1);
namespace Engelsystem\Test\Unit\Events\Listener;
use Engelsystem\Events\Listener\Messages;
use Engelsystem\Mail\EngelsystemMailer;
use Engelsystem\Models\Message;
use Engelsystem\Models\User\Settings;
use Engelsystem\Models\User\User;
use Engelsystem\Test\Unit\HasDatabase;
use Engelsystem\Test\Unit\TestCase;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\Test\TestLogger;
use Symfony\Component\Mailer\Exception\TransportException;
class MessagesTest extends TestCase
{
use HasDatabase;
protected TestLogger $log;
/**
* @covers \Engelsystem\Events\Listener\Messages::created
* @covers \Engelsystem\Events\Listener\Messages::__construct
* @covers \Engelsystem\Events\Listener\Messages::sendMail
*/
public function testCreated(): void
{
/** @var EngelsystemMailer|MockObject $mailer */
$mailer = $this->createMock(EngelsystemMailer::class);
/** @var User $user */
$user = User::factory()
->has(Settings::factory([
'email_messages' => true,
]))
->create();
$message = Message::factory()->create(['receiver_id' => $user->id]);
$mailer->expects($this->once())
->method('sendViewTranslated')
->willReturnCallback(function (
User $receiver,
string $subject,
string $template,
array $data
) use ($user): void {
$this->assertEquals($user->id, $receiver->id);
$this->assertEquals('notification.messages.new', $subject);
$this->assertEquals('emails/messages-new', $template);
$this->assertArrayHasKey('username', $data);
$this->assertArrayHasKey('sender', $data);
$this->assertArrayHasKey('send_message', $data);
});
$handler = new Messages($this->log, $mailer);
$handler->created($message);
}
/**
* @covers \Engelsystem\Events\Listener\Messages::created
*/
public function testCreatedNoEmail(): void
{
/** @var EngelsystemMailer|MockObject $mailer */
$mailer = $this->createMock(EngelsystemMailer::class);
/** @var User $user */
$user = User::factory()
->has(Settings::factory([
'email_messages' => false,
]))
->create();
$message = Message::factory()->create(['receiver_id' => $user->id]);
$mailer->expects($this->never())->method('sendViewTranslated');
$handler = new Messages($this->log, $mailer);
$handler->created($message);
}
/**
* @covers \Engelsystem\Events\Listener\Messages::sendMail
*/
public function testSendMailExceptionHandling(): void
{
/** @var EngelsystemMailer|MockObject $mailer */
$mailer = $this->createMock(EngelsystemMailer::class);
/** @var User $user */
$user = User::factory()
->has(Settings::factory([
'email_messages' => true,
]))
->create();
$message = Message::factory()->create(['receiver_id' => $user->id]);
$mailer->expects($this->once())
->method('sendViewTranslated')
->willReturnCallback(function (): void {
throw new TransportException();
});
$handler = new Messages($this->log, $mailer);
$handler->created($message);
$this->assertTrue($this->log->hasErrorThatContains('Unable to send email'));
}
protected function setUp(): void
{
$this->log = new TestLogger();
parent::setUp();
$this->initDatabase();
}
}