diff --git a/src/app/Console/Development/TemplateRender.php b/src/app/Console/Development/TemplateRender.php --- a/src/app/Console/Development/TemplateRender.php +++ b/src/app/Console/Development/TemplateRender.php @@ -11,7 +11,7 @@ * * @var string */ - protected $signature = 'template:render {template} {--html} {--pdf}'; + protected $signature = 'template:render {template} {--html} {--text} {--pdf}'; /** * The console command description. @@ -52,6 +52,8 @@ $mode = 'html'; if (!empty($this->option('pdf'))) { $mode = 'pdf'; + } elseif (!empty($this->option('text'))) { + $mode = 'text'; } echo $class::fakeRender($mode); diff --git a/src/app/Documents/Receipt.php b/src/app/Documents/Receipt.php --- a/src/app/Documents/Receipt.php +++ b/src/app/Documents/Receipt.php @@ -42,6 +42,8 @@ /** * Render the mail template with fake data * + * @param string $type Output format ('html' or 'pdf') + * * @return string HTML or PDF output */ public static function fakeRender(string $type = 'html'): string @@ -56,6 +58,8 @@ if ($type == 'pdf') { return $receipt->pdfOutput(); + } elseif ($type !== 'html') { + throw new \Exception("Unsupported output format"); } return $receipt->htmlOutput(); diff --git a/src/app/Mail/Helper.php b/src/app/Mail/Helper.php new file mode 100644 --- /dev/null +++ b/src/app/Mail/Helper.php @@ -0,0 +1,33 @@ +build(); // @phpstan-ignore-line + + $mailer = \Illuminate\Container\Container::getInstance()->make('mailer'); + + return $mailer->render(['text' => $mail->textView], $mail->buildViewData()); + } elseif ($type != 'html') { + throw new \Exception("Unsupported output format"); + } + + // HTML output + return $mail->build()->render(); // @phpstan-ignore-line + } +} diff --git a/src/app/Mail/NegativeBalance.php b/src/app/Mail/NegativeBalance.php --- a/src/app/Mail/NegativeBalance.php +++ b/src/app/Mail/NegativeBalance.php @@ -40,7 +40,8 @@ $subject = \trans('mail.negativebalance-subject', ['site' => \config('app.name')]); - $this->view('emails.negative_balance') + $this->view('emails.html.negative_balance') + ->text('emails.plain.negative_balance') ->subject($subject) ->with([ 'site' => \config('app.name'), @@ -56,14 +57,16 @@ /** * Render the mail template with fake data * - * @return string HTML output + * @param string $type Output format ('html' or 'text') + * + * @return string HTML or Plain Text output */ - public static function fakeRender(): string + public static function fakeRender(string $type = 'html'): string { $user = new User(); $mail = new self($user); - return $mail->build()->render(); + return Helper::render($mail, $type); } } diff --git a/src/app/Mail/PasswordReset.php b/src/app/Mail/PasswordReset.php --- a/src/app/Mail/PasswordReset.php +++ b/src/app/Mail/PasswordReset.php @@ -42,7 +42,8 @@ sprintf('/login/reset/%s-%s', $this->code->short_code, $this->code->code) ); - $this->view('emails.password_reset') + $this->view('emails.html.password_reset') + ->text('emails.plain.password_reset') ->subject(__('mail.passwordreset-subject', ['site' => \config('app.name')])) ->with([ 'site' => \config('app.name'), @@ -58,9 +59,11 @@ /** * Render the mail template with fake data * - * @return string HTML output + * @param string $type Output format ('html' or 'text') + * + * @return string HTML or Plain Text output */ - public static function fakeRender(): string + public static function fakeRender(string $type = 'html'): string { $code = new VerificationCode([ 'code' => Str::random(VerificationCode::CODE_LENGTH), @@ -73,6 +76,6 @@ $mail = new self($code); - return $mail->build()->render(); + return Helper::render($mail, $type); } } diff --git a/src/app/Mail/PaymentFailure.php b/src/app/Mail/PaymentFailure.php --- a/src/app/Mail/PaymentFailure.php +++ b/src/app/Mail/PaymentFailure.php @@ -46,7 +46,8 @@ $subject = \trans('mail.paymentfailure-subject', ['site' => \config('app.name')]); - $this->view('emails.payment_failure') + $this->view('emails.html.payment_failure') + ->text('emails.plain.payment_failure') ->subject($subject) ->with([ 'site' => \config('app.name'), @@ -62,21 +63,19 @@ /** * Render the mail template with fake data * - * @return string HTML output + * @param string $type Output format ('html' or 'text') + * + * @return string HTML or Plain Text output */ - public static function fakeRender(): string + public static function fakeRender(string $type = 'mail'): string { $payment = new Payment(); $user = new User([ 'email' => 'test@' . \config('app.domain'), ]); - if (!\config('app.support_url')) { - \config(['app.support_url' => 'https://not-configured-support.url']); - } - $mail = new self($payment, $user); - return $mail->build()->render(); + return Helper::render($mail, $type); } } diff --git a/src/app/Mail/PaymentMandateDisabled.php b/src/app/Mail/PaymentMandateDisabled.php --- a/src/app/Mail/PaymentMandateDisabled.php +++ b/src/app/Mail/PaymentMandateDisabled.php @@ -46,7 +46,8 @@ $subject = \trans('mail.paymentmandatedisabled-subject', ['site' => \config('app.name')]); - $this->view('emails.payment_mandate_disabled') + $this->view('emails.html.payment_mandate_disabled') + ->text('emails.plain.payment_mandate_disabled') ->subject($subject) ->with([ 'site' => \config('app.name'), @@ -62,21 +63,19 @@ /** * Render the mail template with fake data * - * @return string HTML output + * @param string $type Output format ('html' or 'text') + * + * @return string HTML or Plain Text output */ - public static function fakeRender(): string + public static function fakeRender(string $type = 'html'): string { $wallet = new Wallet(); $user = new User([ 'email' => 'test@' . \config('app.domain'), ]); - if (!\config('app.support_url')) { - \config(['app.support_url' => 'https://not-configured-support.url']); - } - $mail = new self($wallet, $user); - return $mail->build()->render(); + return Helper::render($mail, $type); } } diff --git a/src/app/Mail/PaymentSuccess.php b/src/app/Mail/PaymentSuccess.php --- a/src/app/Mail/PaymentSuccess.php +++ b/src/app/Mail/PaymentSuccess.php @@ -46,7 +46,8 @@ $subject = \trans('mail.paymentsuccess-subject', ['site' => \config('app.name')]); - $this->view('emails.payment_success') + $this->view('emails.html.payment_success') + ->text('emails.plain.payment_success') ->subject($subject) ->with([ 'site' => \config('app.name'), @@ -62,21 +63,19 @@ /** * Render the mail template with fake data * - * @return string HTML output + * @param string $type Output format ('html' or 'text') + * + * @return string HTML or Plain Text output */ - public static function fakeRender(): string + public static function fakeRender(string $type = 'html'): string { $payment = new Payment(); $user = new User([ 'email' => 'test@' . \config('app.domain'), ]); - if (!\config('app.support_url')) { - \config(['app.support_url' => 'https://not-configured-support.url']); - } - $mail = new self($payment, $user); - return $mail->build()->render(); + return Helper::render($mail, $type); } } diff --git a/src/app/Mail/SignupVerification.php b/src/app/Mail/SignupVerification.php --- a/src/app/Mail/SignupVerification.php +++ b/src/app/Mail/SignupVerification.php @@ -47,14 +47,15 @@ } $username = trim($username); - $this->view('emails.signup_code') + $this->view('emails.html.signup_code') + ->text('emails.plain.signup_code') ->subject(__('mail.signupcode-subject', ['site' => \config('app.name')])) ->with([ 'site' => \config('app.name'), 'username' => $username ?: 'User', 'code' => $this->code->code, 'short_code' => $this->code->short_code, - 'link' => sprintf('%s', $href, $href), + 'href' => $href, ]); return $this; @@ -63,9 +64,11 @@ /** * Render the mail template with fake data * - * @return string HTML output + * @param string $type Output format ('html' or 'text') + * + * @return string HTML or Plain Text output */ - public static function fakeRender(): string + public static function fakeRender(string $type = 'html'): string { $code = new SignupCode([ 'code' => Str::random(SignupCode::CODE_LENGTH), @@ -77,9 +80,8 @@ ], ]); - $mail = new self($code); - return $mail->build()->render(); + return Helper::render($mail, $type); } } diff --git a/src/app/Mail/SuspendedDebtor.php b/src/app/Mail/SuspendedDebtor.php --- a/src/app/Mail/SuspendedDebtor.php +++ b/src/app/Mail/SuspendedDebtor.php @@ -40,12 +40,15 @@ $subject = \trans('mail.suspendeddebtor-subject', ['site' => \config('app.name')]); - $moreInfo = null; + $moreInfoHtml = null; + $moreInfoText = null; if ($moreInfoUrl = \config('app.kb.account_suspended')) { - $moreInfo = \trans('mail.more-info-html', ['href' => $moreInfoUrl]); + $moreInfoHtml = \trans('mail.more-info-html', ['href' => $moreInfoUrl]); + $moreInfoText = \trans('mail.more-info-text', ['href' => $moreInfoUrl]); } - $this->view('emails.suspended_debtor') + $this->view('emails.html.suspended_debtor') + ->text('emails.plain.suspended_debtor') ->subject($subject) ->with([ 'site' => \config('app.name'), @@ -54,7 +57,8 @@ 'cancelUrl' => \config('app.kb.account_delete'), 'supportUrl' => \config('app.support_url'), 'walletUrl' => Utils::serviceUrl('/wallet'), - 'moreInfo' => $moreInfo, + 'moreInfoHtml' => $moreInfoHtml, + 'moreInfoText' => $moreInfoText, 'days' => 14 // TODO: Configurable ]); @@ -64,26 +68,16 @@ /** * Render the mail template with fake data * - * @return string HTML output + * @param string $type Output format ('html' or 'text') + * + * @return string HTML or Plain Text output */ - public static function fakeRender(): string + public static function fakeRender(string $type = 'html'): string { $user = new User(); - if (!\config('app.support_url')) { - \config(['app.support_url' => 'https://not-configured-support.url']); - } - - if (!\config('app.kb.account_delete')) { - \config(['app.kb.account_delete' => 'https://not-configured-kb.url']); - } - - if (!\config('app.kb.account_suspended')) { - \config(['app.kb.account_suspended' => 'https://not-configured-kb.url']); - } - $mail = new self($user); - return $mail->build()->render(); + return Helper::render($mail, $type); } } diff --git a/src/composer.json b/src/composer.json --- a/src/composer.json +++ b/src/composer.json @@ -22,7 +22,7 @@ "iatstuti/laravel-nullable-fields": "*", "kolab/net_ldap3": "dev-master", "laravel/framework": "6.*", - "laravel/tinker": "^1.0", + "laravel/tinker": "^2.4", "mollie/laravel-mollie": "^2.9", "morrislaptop/laravel-queue-clear": "^1.2", "silviolleite/laravelpwa": "^1.0", @@ -39,10 +39,11 @@ "beyondcode/laravel-er-diagram-generator": "^1.3", "filp/whoops": "^2.0", "fzaninotto/faker": "^1.4", + "kirschbaum-development/mail-intercept": "^0.2.4", "laravel/dusk": "~5.11.0", "mockery/mockery": "^1.0", "nunomaduro/larastan": "^0.5", - "phpstan/phpstan": "^0.12", + "phpstan/phpstan": "0.12.25", "phpunit/phpunit": "^8" }, "config": { diff --git a/src/resources/lang/en/mail.php b/src/resources/lang/en/mail.php --- a/src/resources/lang/en/mail.php +++ b/src/resources/lang/en/mail.php @@ -14,11 +14,12 @@ 'footer' => "Best regards,\nYour :site Team", 'more-info-html' => "See here for more information.", + 'more-info-text' => "See :href for more information.", 'negativebalance-subject' => ":site Payment Reminder", 'negativebalance-body' => "It has probably skipped your attention that you are behind on paying for your :site account. " - . "Consider setting up auto-payment to avoid messages like this in the future.\n\n" - . "Settle up to keep your account running.", + . "Consider setting up auto-payment to avoid messages like this in the future.", + 'negativebalance-body-extd' => "Settle up to keep your account running.", 'passwordreset-subject' => ":site Password Reset", 'passwordreset-body' => "Someone recently asked to change your :site password.\n" @@ -29,16 +30,16 @@ 'paymentmandatedisabled-subject' => ":site Auto-payment Problem", 'paymentmandatedisabled-body' => "Your :site account balance is negative " . "and the configured amount for automatically topping up the balance does not cover " - . "the costs of subscriptions consumed.\n\n" - . "Charging you multiple times for the same amount in short succession " + . "the costs of subscriptions consumed.", + 'paymentmandatedisabled-body-ext' => "Charging you multiple times for the same amount in short succession " . "could lead to issues with the payment provider. " . "In order to not cause any problems, we suspended auto-payment for your account. " . "To resolve this issue, login to your account settings and adjust your auto-payment amount.", 'paymentfailure-subject' => ":site Payment Failed", 'paymentfailure-body' => "Something went wrong with auto-payment for your :site account.\n" - . "We tried to charge you via your preferred payment method, but the charge did not go through.\n\n" - . "In order to not cause any further issues, we suspended auto-payment for your account. " + . "We tried to charge you via your preferred payment method, but the charge did not go through.", + 'paymentfailure-body-ext' => "In order to not cause any further issues, we suspended auto-payment for your account. " . "To resolve this issue, login to your account settings at", 'paymentfailure-body-rest' => "There you can pay manually for your account and " . "change your auto-payment settings.", @@ -47,8 +48,8 @@ 'paymentsuccess-body' => "The auto-payment for your :site account went through without issues. " . "You can check your new account balance and more details here:", - 'support' => "Special circumstances? Something wrong with a charge?\n" - . " :site Support is here to help:", + 'support' => "Special circumstances? Something is wrong with a charge?\n" + . ":site Support is here to help.", 'signupcode-subject' => ":site Registration", 'signupcode-body' => "This is your verification code for the :site registration process: :code.\n" diff --git a/src/resources/views/documents/receipt.blade.php b/src/resources/views/documents/receipt.blade.php --- a/src/resources/views/documents/receipt.blade.php +++ b/src/resources/views/documents/receipt.blade.php @@ -3,7 +3,7 @@ diff --git a/src/resources/views/emails/negative_balance.blade.php b/src/resources/views/emails/html/negative_balance.blade.php rename from src/resources/views/emails/negative_balance.blade.php rename to src/resources/views/emails/html/negative_balance.blade.php --- a/src/resources/views/emails/negative_balance.blade.php +++ b/src/resources/views/emails/html/negative_balance.blade.php @@ -7,6 +7,7 @@

{{ __('mail.header', ['name' => $username]) }}

{{ __('mail.negativebalance-body', ['site' => $site]) }}

+

{{ __('mail.negativebalance-body-ext', ['site' => $site]) }}

{{ $walletUrl }}

@if ($supportUrl) @@ -14,6 +15,6 @@

{{ $supportUrl }}

@endif -

{{ __('mail.footer', ['site' => $site, 'appurl' => config('app.url')]) }}

+

{{ __('mail.footer', ['site' => $site]) }}

diff --git a/src/resources/views/emails/password_reset.blade.php b/src/resources/views/emails/html/password_reset.blade.php rename from src/resources/views/emails/password_reset.blade.php rename to src/resources/views/emails/html/password_reset.blade.php --- a/src/resources/views/emails/password_reset.blade.php +++ b/src/resources/views/emails/html/password_reset.blade.php @@ -10,6 +10,6 @@

{!! $link !!}

-

{{ __('mail.footer', ['site' => $site, 'appurl' => config('app.url')]) }}

+

{{ __('mail.footer', ['site' => $site]) }}

diff --git a/src/resources/views/emails/payment_failure.blade.php b/src/resources/views/emails/html/payment_failure.blade.php rename from src/resources/views/emails/payment_failure.blade.php rename to src/resources/views/emails/html/payment_failure.blade.php --- a/src/resources/views/emails/payment_failure.blade.php +++ b/src/resources/views/emails/html/payment_failure.blade.php @@ -7,6 +7,7 @@

{{ __('mail.header', ['name' => $username]) }}

{{ __('mail.paymentfailure-body', ['site' => $site]) }}

+

{{ __('mail.paymentfailure-body-ext', ['site' => $site]) }}

{{ $walletUrl }}

{{ __('mail.paymentfailure-body-rest', ['site' => $site]) }}

@@ -15,6 +16,6 @@

{{ $supportUrl }}

@endif -

{{ __('mail.footer', ['site' => $site, 'appurl' => config('app.url')]) }}

+

{{ __('mail.footer', ['site' => $site]) }}

diff --git a/src/resources/views/emails/payment_mandate_disabled.blade.php b/src/resources/views/emails/html/payment_mandate_disabled.blade.php rename from src/resources/views/emails/payment_mandate_disabled.blade.php rename to src/resources/views/emails/html/payment_mandate_disabled.blade.php --- a/src/resources/views/emails/payment_mandate_disabled.blade.php +++ b/src/resources/views/emails/html/payment_mandate_disabled.blade.php @@ -7,6 +7,7 @@

{{ __('mail.header', ['name' => $username]) }}

{{ __('mail.paymentmandatedisabled-body', ['site' => $site]) }}

+

{{ __('mail.paymentmandatedisabled-body-ext', ['site' => $site]) }}

{{ $walletUrl }}

{{ __('mail.paymentfailure-body-rest', ['site' => $site]) }}

@@ -15,6 +16,6 @@

{{ $supportUrl }}

@endif -

{{ __('mail.footer', ['site' => $site, 'appurl' => config('app.url')]) }}

+

{{ __('mail.footer', ['site' => $site]) }}

diff --git a/src/resources/views/emails/payment_success.blade.php b/src/resources/views/emails/html/payment_success.blade.php rename from src/resources/views/emails/payment_success.blade.php rename to src/resources/views/emails/html/payment_success.blade.php --- a/src/resources/views/emails/payment_success.blade.php +++ b/src/resources/views/emails/html/payment_success.blade.php @@ -14,6 +14,6 @@

{{ $supportUrl }}

@endif -

{{ __('mail.footer', ['site' => $site, 'appurl' => config('app.url')]) }}

+

{{ __('mail.footer', ['site' => $site]) }}

diff --git a/src/resources/views/emails/signup_code.blade.php b/src/resources/views/emails/html/signup_code.blade.php rename from src/resources/views/emails/signup_code.blade.php rename to src/resources/views/emails/html/signup_code.blade.php --- a/src/resources/views/emails/signup_code.blade.php +++ b/src/resources/views/emails/html/signup_code.blade.php @@ -8,8 +8,8 @@

{{ __('mail.signupcode-body', ['code' => $short_code, 'site' => $site]) }}

-

{!! $link !!}

+

{!! $href !!}

-

{{ __('mail.footer', ['site' => $site, 'appurl' => config('app.url')]) }}

+

{{ __('mail.footer', ['site' => $site]) }}

diff --git a/src/resources/views/emails/suspended_debtor.blade.php b/src/resources/views/emails/html/suspended_debtor.blade.php rename from src/resources/views/emails/suspended_debtor.blade.php rename to src/resources/views/emails/html/suspended_debtor.blade.php --- a/src/resources/views/emails/suspended_debtor.blade.php +++ b/src/resources/views/emails/html/suspended_debtor.blade.php @@ -6,7 +6,7 @@

{{ __('mail.header', ['name' => $username]) }}

-

{{ __('mail.suspendeddebtor-body', ['site' => $site, 'days' => $days]) }} {!! $moreInfo !!}

+

{{ __('mail.suspendeddebtor-body', ['site' => $site, 'days' => $days]) }} {!! $moreInfoHtml !!}

{{ __('mail.suspendeddebtor-middle') }}

{{ $walletUrl }}

@@ -19,6 +19,6 @@

{{ $cancelUrl }}

@endif -

{{ __('mail.footer', ['site' => $site, 'appurl' => config('app.url')]) }}

+

{{ __('mail.footer', ['site' => $site]) }}

diff --git a/src/resources/views/emails/plain/negative_balance.blade.php b/src/resources/views/emails/plain/negative_balance.blade.php new file mode 100644 --- /dev/null +++ b/src/resources/views/emails/plain/negative_balance.blade.php @@ -0,0 +1,16 @@ +{!! __('mail.header', ['name' => $username]) !!} + +{!! __('mail.negativebalance-body', ['site' => $site]) !!} + +{!! __('mail.negativebalance-body-ext', ['site' => $site]) !!} + +{!! $walletUrl !!} + +@if ($supportUrl) +{!! __('mail.support', ['site' => $site]) !!} + +{!! $supportUrl !!} +@endif + +-- +{!! __('mail.footer', ['site' => $site]) !!} diff --git a/src/resources/views/emails/plain/password_reset.blade.php b/src/resources/views/emails/plain/password_reset.blade.php new file mode 100644 --- /dev/null +++ b/src/resources/views/emails/plain/password_reset.blade.php @@ -0,0 +1,8 @@ +{!! __('mail.header', ['name' => $username]) !!} + +{!! __('mail.passwordreset-body', ['code' => $short_code, 'site' => $site]) !!} + +{!! $link !!} + +-- +{!! __('mail.footer', ['site' => $site]) !!} diff --git a/src/resources/views/emails/plain/payment_failure.blade.php b/src/resources/views/emails/plain/payment_failure.blade.php new file mode 100644 --- /dev/null +++ b/src/resources/views/emails/plain/payment_failure.blade.php @@ -0,0 +1,18 @@ +{!! __('mail.header', ['name' => $username]) !!} + +{!! __('mail.paymentfailure-body', ['site' => $site]) !!} + +{!! __('mail.paymentfailure-body-ext', ['site' => $site]) !!} + +{!! $walletUrl !!} + +{!! __('mail.paymentfailure-body-rest', ['site' => $site]) !!} + +@if ($supportUrl) +{!! __('mail.support', ['site' => $site]) !!} + +{!! $supportUrl !!} +@endif + +-- +{!! __('mail.footer', ['site' => $site]) !!} diff --git a/src/resources/views/emails/plain/payment_mandate_disabled.blade.php b/src/resources/views/emails/plain/payment_mandate_disabled.blade.php new file mode 100644 --- /dev/null +++ b/src/resources/views/emails/plain/payment_mandate_disabled.blade.php @@ -0,0 +1,18 @@ +{!! __('mail.header', ['name' => $username]) !!} + +{!! __('mail.paymentmandatedisabled-body', ['site' => $site]) !!} + +{!! __('mail.paymentmandatedisabled-body-ext', ['site' => $site]) !!} + +{!! $walletUrl !!} + +{!! __('mail.paymentfailure-body-rest', ['site' => $site]) !!} + +@if ($supportUrl) +{!! __('mail.support', ['site' => $site]) !!} + +{!! $supportUrl !!} +@endif + +-- +{!! __('mail.footer', ['site' => $site]) !!} diff --git a/src/resources/views/emails/plain/payment_success.blade.php b/src/resources/views/emails/plain/payment_success.blade.php new file mode 100644 --- /dev/null +++ b/src/resources/views/emails/plain/payment_success.blade.php @@ -0,0 +1,14 @@ +{!! __('mail.header', ['name' => $username]) !!} + +{!! __('mail.paymentsuccess-body', ['site' => $site]) !!} + +{!! $walletUrl !!} + +@if ($supportUrl) +{!! __('mail.support', ['site' => $site]) !!} + +{!! $supportUrl !!} +@endif + +-- +{!! __('mail.footer', ['site' => $site]) !!} diff --git a/src/resources/views/emails/plain/signup_code.blade.php b/src/resources/views/emails/plain/signup_code.blade.php new file mode 100644 --- /dev/null +++ b/src/resources/views/emails/plain/signup_code.blade.php @@ -0,0 +1,8 @@ +{!! __('mail.header', ['name' => $username]) !!} + +{!! __('mail.signupcode-body', ['code' => $short_code, 'site' => $site]) !!} + +{!! $href !!} + +-- +{!! __('mail.footer', ['site' => $site]) !!} diff --git a/src/resources/views/emails/plain/suspended_debtor.blade.php b/src/resources/views/emails/plain/suspended_debtor.blade.php new file mode 100644 --- /dev/null +++ b/src/resources/views/emails/plain/suspended_debtor.blade.php @@ -0,0 +1,22 @@ +{!! __('mail.header', ['name' => $username]) !!} + +{!! __('mail.suspendeddebtor-body', ['site' => $site, 'days' => $days]) !!} {!! $moreInfoText !!} + +{!! __('mail.suspendeddebtor-middle') !!} + +{!! $walletUrl !!} + +@if ($supportUrl) +{!! __('mail.support', ['site' => $site]) !!} + +{!! $supportUrl !!} +@endif +@if ($cancelUrl) + +{!! __('mail.suspendeddebtor-cancel') !!} + +{!! $cancelUrl !!} +@endif + +-- +{!! __('mail.footer', ['site' => $site]) !!} diff --git a/src/tests/MailInterceptTrait.php b/src/tests/MailInterceptTrait.php new file mode 100644 --- /dev/null +++ b/src/tests/MailInterceptTrait.php @@ -0,0 +1,79 @@ +interceptMail(); + + Mail::send($mail); + + $message = $this->interceptedMail()->first(); + + // SwiftMailer does not have methods to get the bodies, we'll parse the message + list($plain, $html) = $this->extractMailBody($message->toString()); + + return [ + 'plain' => $plain, + 'html' => $html, + 'message' => $message, + ]; + } + + /** + * Simple message parser to extract plain and html body + * + * @param string $message Email message as string + * + * @return array Plain text and HTML body + */ + protected function extractMailBody(string $message): array + { + // Note that we're not supporting every message format, we only + // support what Laravel/SwiftMailer produces + // TODO: It may stop working if we start using attachments + $plain = ''; + $html = ''; + + if (preg_match('/[\s\t]boundary="([^"]+)"/', $message, $matches)) { + // multipart message assume plain and html parts + $split = preg_split('/--' . preg_quote($matches[1]) . '/', $message); + + list($plain_head, $plain) = explode("\r\n\r\n", $split[1], 2); + list($html_head, $html) = explode("\r\n\r\n", $split[2], 2); + + if (strpos($plain_head, 'Content-Transfer-Encoding: quoted-printable') !== false) { + $plain = quoted_printable_decode($plain); + } + + if (strpos($html_head, 'Content-Transfer-Encoding: quoted-printable') !== false) { + $html = quoted_printable_decode($html); + } + } else { + list($header, $html) = explode("\r\n\r\n", $message, 2); + if (strpos($header, 'Content-Transfer-Encoding: quoted-printable') !== false) { + $html = quoted_printable_decode($html); + } + } + + return [$plain, $html]; + } +} diff --git a/src/tests/Unit/Mail/NegativeBalanceTest.php b/src/tests/Unit/Mail/NegativeBalanceTest.php --- a/src/tests/Unit/Mail/NegativeBalanceTest.php +++ b/src/tests/Unit/Mail/NegativeBalanceTest.php @@ -4,16 +4,17 @@ use App\Mail\NegativeBalance; use App\User; +use Tests\MailInterceptTrait; use Tests\TestCase; class NegativeBalanceTest extends TestCase { + use MailInterceptTrait; + /** * Test email content - * - * @return void */ - public function testBuild() + public function testBuild(): void { $user = new User(); @@ -21,17 +22,19 @@ 'app.support_url' => 'https://kolab.org/support', ]); - $mail = new NegativeBalance($user); - $html = $mail->build()->render(); + $mail = $this->fakeMail(new NegativeBalance($user)); + + $html = $mail['html']; + $plain = $mail['plain']; $walletUrl = \App\Utils::serviceUrl('/wallet'); $walletLink = sprintf('%s', $walletUrl, $walletUrl); $supportUrl = \config('app.support_url'); $supportLink = sprintf('%s', $supportUrl, $supportUrl); - $appName = \config('app.name'); - $this->assertSame("$appName Payment Reminder", $mail->subject); + $this->assertMailSubject("$appName Payment Reminder", $mail['message']); + $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); $this->assertTrue(strpos($html, $walletLink) > 0); @@ -39,5 +42,12 @@ $this->assertTrue(strpos($html, "behind on paying for your $appName account") > 0); $this->assertTrue(strpos($html, "$appName Support") > 0); $this->assertTrue(strpos($html, "$appName Team") > 0); + + $this->assertStringStartsWith('Dear ' . $user->name(true), $plain); + $this->assertTrue(strpos($plain, $walletUrl) > 0); + $this->assertTrue(strpos($plain, $supportUrl) > 0); + $this->assertTrue(strpos($plain, "behind on paying for your $appName account") > 0); + $this->assertTrue(strpos($plain, "$appName Support") > 0); + $this->assertTrue(strpos($plain, "$appName Team") > 0); } } diff --git a/src/tests/Unit/Mail/PasswordResetTest.php b/src/tests/Unit/Mail/PasswordResetTest.php --- a/src/tests/Unit/Mail/PasswordResetTest.php +++ b/src/tests/Unit/Mail/PasswordResetTest.php @@ -6,16 +6,17 @@ use App\User; use App\Utils; use App\VerificationCode; +use Tests\MailInterceptTrait; use Tests\TestCase; class PasswordResetTest extends TestCase { + use MailInterceptTrait; + /** * Test email content - * - * @return void */ - public function testPasswordResetBuild() + public function testBuild(): void { $code = new VerificationCode([ 'user_id' => 123456789, @@ -24,20 +25,26 @@ 'short_code' => 'short-code', ]); - // @phpstan-ignore-next-line $code->user = new User([ 'name' => 'User Name', ]); - $mail = new PasswordReset($code); - $html = $mail->build()->render(); + $mail = $this->fakeMail(new PasswordReset($code)); + + $html = $mail['html']; + $plain = $mail['plain']; $url = Utils::serviceUrl('/login/reset/' . $code->short_code . '-' . $code->code); $link = "$url"; + $appName = \config('app.name'); + + $this->assertMailSubject("$appName Password Reset", $mail['message']); - $this->assertSame(\config('app.name') . ' Password Reset', $mail->subject); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $link) > 0); $this->assertTrue(strpos($html, $code->user->name(true)) > 0); + + $this->assertStringStartsWith("Dear " . $code->user->name(true), $plain); + $this->assertTrue(strpos($plain, $link) > 0); } } diff --git a/src/tests/Unit/Mail/PaymentFailureTest.php b/src/tests/Unit/Mail/PaymentFailureTest.php --- a/src/tests/Unit/Mail/PaymentFailureTest.php +++ b/src/tests/Unit/Mail/PaymentFailureTest.php @@ -5,26 +5,28 @@ use App\Mail\PaymentFailure; use App\Payment; use App\User; +use Tests\MailInterceptTrait; use Tests\TestCase; class PaymentFailureTest extends TestCase { + use MailInterceptTrait; + /** * Test email content - * - * @return void */ - public function testBuild() + public function testBuild(): void { - // @phpstan-ignore-next-line $user = new User(); $payment = new Payment(); $payment->amount = 123; \config(['app.support_url' => 'https://kolab.org/support']); - $mail = new PaymentFailure($payment, $user); - $html = $mail->build()->render(); + $mail = $this->fakeMail(new PaymentFailure($payment, $user)); + + $html = $mail['html']; + $plain = $mail['plain']; $walletUrl = \App\Utils::serviceUrl('/wallet'); $walletLink = sprintf('%s', $walletUrl, $walletUrl); @@ -32,7 +34,8 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = \config('app.name'); - $this->assertSame("$appName Payment Failed", $mail->subject); + $this->assertMailSubject("$appName Payment Failed", $mail['message']); + $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); $this->assertTrue(strpos($html, $walletLink) > 0); @@ -40,5 +43,12 @@ $this->assertTrue(strpos($html, "$appName Support") > 0); $this->assertTrue(strpos($html, "Something went wrong with auto-payment for your $appName account") > 0); $this->assertTrue(strpos($html, "$appName Team") > 0); + + $this->assertStringStartsWith('Dear ' . $user->name(true), $plain); + $this->assertTrue(strpos($plain, $walletUrl) > 0); + $this->assertTrue(strpos($plain, $supportUrl) > 0); + $this->assertTrue(strpos($plain, "$appName Support") > 0); + $this->assertTrue(strpos($plain, "Something went wrong with auto-payment for your $appName account") > 0); + $this->assertTrue(strpos($plain, "$appName Team") > 0); } } diff --git a/src/tests/Unit/Mail/PaymentMandateDisabledTest.php b/src/tests/Unit/Mail/PaymentMandateDisabledTest.php --- a/src/tests/Unit/Mail/PaymentMandateDisabledTest.php +++ b/src/tests/Unit/Mail/PaymentMandateDisabledTest.php @@ -5,25 +5,27 @@ use App\Mail\PaymentMandateDisabled; use App\Wallet; use App\User; +use Tests\MailInterceptTrait; use Tests\TestCase; class PaymentMandateDisabledTest extends TestCase { + use MailInterceptTrait; + /** * Test email content - * - * @return void */ - public function testBuild() + public function testBuild(): void { - // @phpstan-ignore-next-line $user = new User(); $wallet = new Wallet(); \config(['app.support_url' => 'https://kolab.org/support']); - $mail = new PaymentMandateDisabled($wallet, $user); - $html = $mail->build()->render(); + $mail = $this->fakeMail(new PaymentMandateDisabled($wallet, $user)); + + $html = $mail['html']; + $plain = $mail['plain']; $walletUrl = \App\Utils::serviceUrl('/wallet'); $walletLink = sprintf('%s', $walletUrl, $walletUrl); @@ -31,7 +33,8 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = \config('app.name'); - $this->assertSame("$appName Auto-payment Problem", $mail->subject); + $this->assertMailSubject("$appName Auto-payment Problem", $mail['message']); + $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); $this->assertTrue(strpos($html, $walletLink) > 0); @@ -39,5 +42,12 @@ $this->assertTrue(strpos($html, "$appName Support") > 0); $this->assertTrue(strpos($html, "Your $appName account balance") > 0); $this->assertTrue(strpos($html, "$appName Team") > 0); + + $this->assertStringStartsWith('Dear ' . $user->name(true), $plain); + $this->assertTrue(strpos($plain, $walletUrl) > 0); + $this->assertTrue(strpos($plain, $supportUrl) > 0); + $this->assertTrue(strpos($plain, "$appName Support") > 0); + $this->assertTrue(strpos($plain, "Your $appName account balance") > 0); + $this->assertTrue(strpos($plain, "$appName Team") > 0); } } diff --git a/src/tests/Unit/Mail/PaymentSuccessTest.php b/src/tests/Unit/Mail/PaymentSuccessTest.php --- a/src/tests/Unit/Mail/PaymentSuccessTest.php +++ b/src/tests/Unit/Mail/PaymentSuccessTest.php @@ -5,26 +5,28 @@ use App\Mail\PaymentSuccess; use App\Payment; use App\User; +use Tests\MailInterceptTrait; use Tests\TestCase; class PaymentSuccessTest extends TestCase { + use MailInterceptTrait; + /** * Test email content - * - * @return void */ - public function testBuild() + public function testBuild(): void { - // @phpstan-ignore-next-line $user = new User(); $payment = new Payment(); $payment->amount = 123; \config(['app.support_url' => 'https://kolab.org/support']); - $mail = new PaymentSuccess($payment, $user); - $html = $mail->build()->render(); + $mail = $this->fakeMail(new PaymentSuccess($payment, $user)); + + $html = $mail['html']; + $plain = $mail['plain']; $walletUrl = \App\Utils::serviceUrl('/wallet'); $walletLink = sprintf('%s', $walletUrl, $walletUrl); @@ -32,7 +34,8 @@ $supportLink = sprintf('%s', $supportUrl, $supportUrl); $appName = \config('app.name'); - $this->assertSame("$appName Payment Succeeded", $mail->subject); + $this->assertMailSubject("$appName Payment Succeeded", $mail['message']); + $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); $this->assertTrue(strpos($html, $walletLink) > 0); @@ -40,5 +43,12 @@ $this->assertTrue(strpos($html, "$appName Support") > 0); $this->assertTrue(strpos($html, "The auto-payment for your $appName account") > 0); $this->assertTrue(strpos($html, "$appName Team") > 0); + + $this->assertStringStartsWith('Dear ' . $user->name(true), $plain); + $this->assertTrue(strpos($plain, $walletUrl) > 0); + $this->assertTrue(strpos($plain, $supportUrl) > 0); + $this->assertTrue(strpos($plain, "$appName Support") > 0); + $this->assertTrue(strpos($plain, "The auto-payment for your $appName account") > 0); + $this->assertTrue(strpos($plain, "$appName Team") > 0); } } diff --git a/src/tests/Unit/Mail/SignupVerificationTest.php b/src/tests/Unit/Mail/SignupVerificationTest.php --- a/src/tests/Unit/Mail/SignupVerificationTest.php +++ b/src/tests/Unit/Mail/SignupVerificationTest.php @@ -5,16 +5,17 @@ use App\Mail\SignupVerification; use App\SignupCode; use App\Utils; +use Tests\MailInterceptTrait; use Tests\TestCase; class SignupVerificationTest extends TestCase { + use MailInterceptTrait; + /** * Test email content - * - * @return void */ - public function testSignupVerificationBuild() + public function testBuild(): void { $code = new SignupCode([ 'code' => 'code', @@ -26,15 +27,22 @@ ], ]); - $mail = new SignupVerification($code); - $html = $mail->build()->render(); + $mail = $this->fakeMail(new SignupVerification($code)); + + $html = $mail['html']; + $plain = $mail['plain']; $url = Utils::serviceUrl('/signup/' . $code->short_code . '-' . $code->code); $link = "$url"; + $appName = \config('app.name'); + + $this->assertMailSubject("$appName Registration", $mail['message']); - $this->assertSame(\config('app.name') . ' Registration', $mail->subject); $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $link) > 0); $this->assertTrue(strpos($html, 'First Last') > 0); + + $this->assertStringStartsWith('Dear First Last', $plain); + $this->assertTrue(strpos($plain, $url) > 0); } } diff --git a/src/tests/Unit/Mail/SuspendedDebtorTest.php b/src/tests/Unit/Mail/SuspendedDebtorTest.php --- a/src/tests/Unit/Mail/SuspendedDebtorTest.php +++ b/src/tests/Unit/Mail/SuspendedDebtorTest.php @@ -4,16 +4,17 @@ use App\Mail\SuspendedDebtor; use App\User; +use Tests\MailInterceptTrait; use Tests\TestCase; class SuspendedDebtorTest extends TestCase { + use MailInterceptTrait; + /** * Test email content - * - * @return void */ - public function testBuild() + public function testBuild(): void { $user = new User(); @@ -23,8 +24,10 @@ 'app.kb.account_delete' => 'https://kb.kolab.org/account-delete', ]); - $mail = new SuspendedDebtor($user); - $html = $mail->build()->render(); + $mail = $this->fakeMail(new SuspendedDebtor($user)); + + $html = $mail['html']; + $plain = $mail['plain']; $walletUrl = \App\Utils::serviceUrl('/wallet'); $walletLink = sprintf('%s', $walletUrl, $walletUrl); @@ -34,10 +37,10 @@ $deleteLink = sprintf('%s', $deleteUrl, $deleteUrl); $moreUrl = \config('app.kb.account_suspended'); $moreLink = sprintf('here', $moreUrl); - $appName = \config('app.name'); - $this->assertSame("$appName Account Suspended", $mail->subject); + $this->assertMailSubject("$appName Account Suspended", $mail['message']); + $this->assertStringStartsWith('', $html); $this->assertTrue(strpos($html, $user->name(true)) > 0); $this->assertTrue(strpos($html, $walletLink) > 0); @@ -48,5 +51,15 @@ $this->assertTrue(strpos($html, "See $moreLink for more information") > 0); $this->assertTrue(strpos($html, "$appName Support") > 0); $this->assertTrue(strpos($html, "$appName Team") > 0); + + $this->assertStringStartsWith('Dear ' . $user->name(true), $plain); + $this->assertTrue(strpos($plain, $walletUrl) > 0); + $this->assertTrue(strpos($plain, $supportUrl) > 0); + $this->assertTrue(strpos($plain, $deleteUrl) > 0); + $this->assertTrue(strpos($plain, "You have been behind on paying for your $appName account") > 0); + $this->assertTrue(strpos($plain, "over 14 days") > 0); + $this->assertTrue(strpos($plain, "See $moreUrl for more information") > 0); + $this->assertTrue(strpos($plain, "$appName Support") > 0); + $this->assertTrue(strpos($plain, "$appName Team") > 0); } }