diff --git a/app/Form/FormSettings.php b/app/Form/FormSettings.php index 9f4cdb57..67b3a375 100644 --- a/app/Form/FormSettings.php +++ b/app/Form/FormSettings.php @@ -2,7 +2,6 @@ namespace App\Form; -use App\Form\Actions\SettingStoreAction; use App\Setting\Contracts\Storeable; use App\Setting\LocalSettings; use Lorisleiva\Actions\ActionRequest; @@ -11,6 +10,7 @@ class FormSettings extends LocalSettings implements Storeable { public string $registerUrl; public string $clearCacheUrl; + public ?string $replyToMail; public static function group(): string { @@ -19,7 +19,7 @@ class FormSettings extends LocalSettings implements Storeable public static function title(): string { - return 'Formulare'; + return 'Veranstaltungen'; } /** @@ -30,6 +30,7 @@ class FormSettings extends LocalSettings implements Storeable return [ 'registerUrl' => 'present|string', 'clearCacheUrl' => 'present|string', + 'replyToMail' => 'nullable|string|email', ]; } @@ -47,6 +48,7 @@ class FormSettings extends LocalSettings implements Storeable 'data' => [ 'registerUrl' => $this->registerUrl, 'clearCacheUrl' => $this->clearCacheUrl, + 'replyToMail' => $this->replyToMail, ] ] ]; diff --git a/app/Prevention/Actions/SettingStoreAction.php b/app/Prevention/Actions/SettingStoreAction.php index c04cd68d..5cc80a86 100644 --- a/app/Prevention/Actions/SettingStoreAction.php +++ b/app/Prevention/Actions/SettingStoreAction.php @@ -24,6 +24,7 @@ class SettingStoreAction 'weeks' => 'required|numeric|gte:0', 'freshRememberInterval' => 'required|numeric|gte:0', 'active' => 'boolean', + 'replyToMail' => 'nullable|string|email', ]; } @@ -33,6 +34,7 @@ class SettingStoreAction $settings->formmail = EditorData::from($request->formmail); $settings->yearlymail = EditorData::from($request->yearlymail); $settings->weeks = $request->weeks; + $settings->replyToMail = $request->replyToMail; $settings->freshRememberInterval = $request->freshRememberInterval; $settings->active = $request->active; $settings->yearlyMemberFilter = FilterScope::from($request->yearlyMemberFilter); diff --git a/app/Prevention/Mails/YearlyMail.php b/app/Prevention/Mails/YearlyMail.php index 94b8f84f..7329bb0b 100644 --- a/app/Prevention/Mails/YearlyMail.php +++ b/app/Prevention/Mails/YearlyMail.php @@ -6,6 +6,7 @@ use App\Invoice\InvoiceSettings; use App\Lib\Editor\EditorData; use App\Prevention\Contracts\Preventable; use App\Prevention\Data\PreventionData; +use App\Prevention\PreventionSettings; use Illuminate\Bus\Queueable; use Illuminate\Mail\Attachment; use Illuminate\Mail\Mailable; @@ -19,6 +20,7 @@ class YearlyMail extends Mailable use Queueable, SerializesModels; public InvoiceSettings $settings; + public PreventionSettings $preventionSettings; /** * Create a new message instance. @@ -27,6 +29,7 @@ class YearlyMail extends Mailable public function __construct(public Preventable $preventable, public EditorData $bodyText, public Collection $preventions) { $this->settings = app(InvoiceSettings::class); + $this->preventionSettings = app(PreventionSettings::class); $this->bodyText = $this->bodyText ->replaceWithList('wanted', $preventions->map(fn($prevention) => $prevention->text())->toArray()); } @@ -38,9 +41,15 @@ class YearlyMail extends Mailable */ public function envelope() { - return (new Envelope( + $envelope = (new Envelope( subject: $this->preventable->preventableSubject(), ))->to($this->preventable->getMailRecipient()->email, $this->preventable->getMailRecipient()->name); + + if ($this->preventionSettings->replyToMail !== null) { + $envelope->replyTo($this->preventionSettings->replyToMail); + } + + return $envelope; } /** diff --git a/app/Prevention/PreventionSettings.php b/app/Prevention/PreventionSettings.php index dd2b961f..59eb5875 100644 --- a/app/Prevention/PreventionSettings.php +++ b/app/Prevention/PreventionSettings.php @@ -15,6 +15,7 @@ class PreventionSettings extends LocalSettings public int $freshRememberInterval; public bool $active; public FilterScope $yearlyMemberFilter; + public ?string $replyToMail; /** * @var array * @todo Create collection cast to Collection of enums @@ -49,6 +50,7 @@ class PreventionSettings extends LocalSettings ...$this->toArray(), 'weeks' => (string) $this->weeks, 'freshRememberInterval' => (string) $this->freshRememberInterval, + 'replyToMail' => $this->replyToMail, ]; } } diff --git a/database/settings/202511-07_create_replyto_mail_settings.php b/database/settings/202511-07_create_replyto_mail_settings.php new file mode 100644 index 00000000..3824167c --- /dev/null +++ b/database/settings/202511-07_create_replyto_mail_settings.php @@ -0,0 +1,12 @@ +migrator->add('form.replyToMail', ''); + $this->migrator->add('prevention.replyToMail', ''); + } +}; diff --git a/docker-compose.yml b/docker-compose.yml index aa27c717..48522bec 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -92,6 +92,8 @@ services: socketi: image: quay.io/soketi/soketi:89604f268623cf799573178a7ba56b7491416bde-16-debian + ports: + - "6001:6001" environment: SOKETI_DEFAULT_APP_ID: adremaid SOKETI_DEFAULT_APP_KEY: adremakey @@ -104,6 +106,8 @@ services: meilisearch: image: getmeili/meilisearch:v1.6 + ports: + - "7700:7700" volumes: - ./data/meilisearch:/meili_data env_file: diff --git a/resources/img/svg/external-link.svg b/resources/img/svg/external-link.svg new file mode 100644 index 00000000..5324fd21 --- /dev/null +++ b/resources/img/svg/external-link.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/resources/js/views/setting/Form.vue b/resources/js/views/setting/Form.vue index ff8a95af..8bfd2ebc 100644 --- a/resources/js/views/setting/Form.vue +++ b/resources/js/views/setting/Form.vue @@ -11,6 +11,7 @@
+
diff --git a/resources/js/views/setting/Prevention.vue b/resources/js/views/setting/Prevention.vue index 5c047e68..d5b0d736 100644 --- a/resources/js/views/setting/Prevention.vue +++ b/resources/js/views/setting/Prevention.vue @@ -22,6 +22,7 @@ + diff --git a/tests/EndToEnd/Member/PreventionTest.php b/tests/EndToEnd/Member/PreventionTest.php index 897e0286..8a1696f4 100644 --- a/tests/EndToEnd/Member/PreventionTest.php +++ b/tests/EndToEnd/Member/PreventionTest.php @@ -248,6 +248,17 @@ it('notices a few weeks before', function ($date, bool $shouldSend) { [fn() => now()->subYears(5)->addWeeks(2)->subDay(), false], ]); +it('sets reply to mail', function () { + Mail::fake(); + app(PreventionSettings::class)->fill(['replyToMail' => 'admin@example.com'])->save(); + createMember(['has_vk' => false]); + + sleep(2); + YearlyRememberAction::run(); + + Mail::assertSent(YearlyMail::class, fn ($message) => $message->hasReplyTo('admin@example.com')); +}); + it('remembers members yearly', function ($date, $shouldSend) { Mail::fake(); createMember(['efz' => $date, 'ps_at' => now(), 'has_vk' => true]); diff --git a/tests/Feature/Prevention/SettingTest.php b/tests/Feature/Prevention/SettingTest.php index 8f2704f0..b14a5c30 100644 --- a/tests/Feature/Prevention/SettingTest.php +++ b/tests/Feature/Prevention/SettingTest.php @@ -28,6 +28,7 @@ it('receives settings', function () { 'weeks' => 9, 'freshRememberInterval' => 11, 'active' => true, + 'replyToMail' => 'admin@example.com', 'preventAgainst' => [Prevention::MOREPS->name], 'yearlyMemberFilter' => FilterScope::from([ 'memberships' => [['group_ids' => [33]]], @@ -41,6 +42,7 @@ it('receives settings', function () { ->assertJsonPath('data.weeks', '9') ->assertJsonPath('data.active', true) ->assertJsonPath('data.freshRememberInterval', '11') + ->assertJsonPath('data.replyToMail', 'admin@example.com') ->assertJsonPath('data.yearlyMemberFilter.search', 'searchstring') ->assertJsonPath('data.yearlyMemberFilter.memberships.0.group_ids.0', 33) ->assertJsonPath('data.preventAgainst', ['MOREPS']) @@ -58,6 +60,7 @@ it('testItStoresSettings', function () { 'freshRememberInterval' => 11, 'active' => true, 'preventAgainst' => ['EFZ'], + 'replyToMail' => 'admin@example.com', 'yearlyMemberFilter' => [ 'memberships' => [['group_ids' => 33]], 'search' => 'searchstring', @@ -66,6 +69,7 @@ it('testItStoresSettings', function () { test()->assertTrue(app(PreventionSettings::class)->formmail->hasAll(['new lorem'])); test()->assertTrue(app(PreventionSettings::class)->yearlymail->hasAll(['lala dd'])); test()->assertEquals(9, app(PreventionSettings::class)->weeks); + test()->assertEquals('admin@example.com', app(PreventionSettings::class)->replyToMail); test()->assertEquals(11, app(PreventionSettings::class)->freshRememberInterval); test()->assertTrue(app(PreventionSettings::class)->active); test()->assertEquals([['group_ids' => 33]], app(PreventionSettings::class)->yearlyMemberFilter->memberships);