Make email messages Markdown.

This commit is contained in:
James Cole 2022-03-29 12:45:48 +02:00
parent 9c19a08b17
commit 09bd0b572a
No known key found for this signature in database
GPG Key ID: B49A324B7EAD6D80
71 changed files with 747 additions and 555 deletions

View File

@ -37,21 +37,16 @@ class AdminRequestedTestMessage extends Event
{
use SerializesModels;
/** @var string The users IP address */
public $ipAddress;
/** @var User The user */
public $user;
public User $user;
/**
* Create a new event instance.
*
* @param User $user
* @param string $ipAddress
*/
public function __construct(User $user, string $ipAddress)
public function __construct(User $user)
{
Log::debug(sprintf('Triggered AdminRequestedTestMessage for user #%d (%s) and IP %s!', $user->id, $user->email, $ipAddress));
Log::debug(sprintf('Triggered AdminRequestedTestMessage for user #%d (%s)', $user->id, $user->email));
$this->user = $user;
$this->ipAddress = $ipAddress;
}
}

View File

@ -40,10 +40,8 @@ class RequestedReportOnJournals
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/** @var Collection The transaction groups to report on. */
public $groups;
/** @var int The ID of the user. */
public $userId;
public Collection $groups;
public int $userId;
/**
* Create a new event instance.

View File

@ -36,14 +36,9 @@ class UserChangedEmail extends Event
{
use SerializesModels;
/** @var string The user's IP address */
public $ipAddress;
/** @var string The user's new email address */
public $newEmail;
/** @var string The user's old email address */
public $oldEmail;
/** @var User The user itself */
public $user;
public string $newEmail;
public string $oldEmail;
public User $user;
/**
* UserChangedEmail constructor.
@ -51,12 +46,10 @@ class UserChangedEmail extends Event
* @param User $user
* @param string $newEmail
* @param string $oldEmail
* @param string $ipAddress
*/
public function __construct(User $user, string $newEmail, string $oldEmail, string $ipAddress)
public function __construct(User $user, string $newEmail, string $oldEmail)
{
$this->user = $user;
$this->ipAddress = $ipAddress;
$this->oldEmail = $oldEmail;
$this->newEmail = $newEmail;
}

View File

@ -59,18 +59,16 @@ class APIEventHandler
$email = config('firefly.site_owner');
}
$ipAddress = Request::ip();
// see if user has alternative email address:
$pref = app('preferences')->getForUser($user, 'remote_guard_alt_email');
if (null !== $pref) {
$email = (string)(is_array($pref->data) ? $email : $pref->data);
}
Log::debug(sprintf('Now in APIEventHandler::accessTokenCreated. Email is %s, IP is %s', $email, $ipAddress));
Log::debug(sprintf('Now in APIEventHandler::accessTokenCreated. Email is %s', $email));
try {
Log::debug('Trying to send message...');
Mail::to($email)->send(new AccessTokenCreatedMail($email, $ipAddress));
Mail::to($email)->send(new AccessTokenCreatedMail);
} catch (Exception $e) { // @phpstan-ignore-line
Log::debug('Send message failed! :(');

View File

@ -52,7 +52,6 @@ class AdminEventHandler
// is user even admin?
if ($repository->hasRole($event->user, 'owner')) {
$email = $event->user->email;
$ipAddress = $event->ipAddress;
// if user is demo user, send to owner:
if ($event->user->hasRole('demo')) {
@ -65,10 +64,10 @@ class AdminEventHandler
$email = $pref->data;
}
Log::debug(sprintf('Now in sendTestMessage event handler. Email is %s, IP is %s', $email, $ipAddress));
Log::debug(sprintf('Now in sendTestMessage event handler. Email is %s', $email));
try {
Log::debug('Trying to send message...');
Mail::to($email)->send(new AdminTestMail($email, $ipAddress));
Mail::to($email)->send(new AdminTestMail($email));
// Laravel cannot pretend this process failed during testing.
} catch (Exception $e) { // @phpstan-ignore-line

View File

@ -58,23 +58,9 @@ class AutomationHandler
$repository = app(UserRepositoryInterface::class);
$user = $repository->find($event->userId);
if (null !== $user && 0 !== $event->groups->count()) {
$email = $user->email;
// see if user has alternative email address:
$pref = app('preferences')->getForUser($user, 'remote_guard_alt_email');
if (null !== $pref) {
$email = $pref->data;
}
// if user is demo user, send to owner:
if ($user->hasRole('demo')) {
$email = config('firefly.site_owner');
}
try {
Log::debug('Trying to mail...');
Mail::to($user->email)->send(new ReportNewJournalsMail($email, '127.0.0.1', $event->groups));
Mail::to($user->email)->send(new ReportNewJournalsMail($event->groups));
} catch (Exception $e) { // @phpstan-ignore-line
Log::debug('Send message failed! :(');

View File

@ -229,11 +229,10 @@ class UserEventHandler
$newEmail = $event->newEmail;
$oldEmail = $event->oldEmail;
$user = $event->user;
$ipAddress = $event->ipAddress;
$token = app('preferences')->getForUser($user, 'email_change_confirm_token', 'invalid');
$uri = route('profile.confirm-email-change', [$token->data]);
try {
Mail::to($newEmail)->send(new ConfirmEmailChangeMail($newEmail, $oldEmail, $uri, $ipAddress));
Mail::to($newEmail)->send(new ConfirmEmailChangeMail($newEmail, $oldEmail, $uri));
} catch (Exception $e) { // @phpstan-ignore-line
Log::error($e->getMessage());
@ -255,12 +254,11 @@ class UserEventHandler
$newEmail = $event->newEmail;
$oldEmail = $event->oldEmail;
$user = $event->user;
$ipAddress = $event->ipAddress;
$token = app('preferences')->getForUser($user, 'email_change_undo_token', 'invalid');
$hashed = hash('sha256', sprintf('%s%s', (string)config('app.key'), $oldEmail));
$uri = route('profile.undo-email-change', [$token->data, $hashed]);
try {
Mail::to($oldEmail)->send(new UndoEmailChangeMail($newEmail, $oldEmail, $uri, $ipAddress));
Mail::to($oldEmail)->send(new UndoEmailChangeMail($newEmail, $oldEmail, $uri));
} catch (Exception $e) { // @phpstan-ignore-line
Log::error($e->getMessage());

View File

@ -83,9 +83,8 @@ class HomeController extends Controller
Log::channel('audit')->info('User sends test message.');
/** @var User $user */
$user = auth()->user();
$ipAddress = $request->ip();
Log::debug(sprintf('Now in testMessage() controller. IP is %s', $ipAddress));
event(new AdminRequestedTestMessage($user, $ipAddress));
Log::debug('Now in testMessage() controller.');
event(new AdminRequestedTestMessage($user));
session()->flash('info', (string)trans('firefly.send_test_triggered'));
return redirect(route('admin.index'));

View File

@ -96,7 +96,7 @@ class RegisterController extends Controller
$this->validator($request->all())->validate();
$user = $this->createUser($request->all());
Log::info(sprintf('Registered new user %s', $user->email));
event(new RegisteredUser($user, $request->ip()));
event(new RegisteredUser($user));
$this->guard()->login($user);

View File

@ -27,6 +27,7 @@ use Exception;
use FireflyIII\Events\RequestedVersionCheckStatus;
use FireflyIII\Helpers\Collector\GroupCollectorInterface;
use FireflyIII\Http\Middleware\Installer;
use FireflyIII\Mail\UndoEmailChangeMail;
use FireflyIII\Models\AccountType;
use FireflyIII\Repositories\Account\AccountRepositoryInterface;
use FireflyIII\Repositories\Bill\BillRepositoryInterface;
@ -35,6 +36,7 @@ use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Log;
use Mail;
/**
* Class HomeController.
@ -72,7 +74,7 @@ class HomeController extends Controller
Log::debug('Received dateRange', ['start' => $request->get('start'), 'end' => $request->get('end'), 'label' => $request->get('label')]);
// check if the label is "everything" or "Custom range" which will betray
// a possible problem with the budgets.
if ($label === (string)trans('firefly.everything') || $label === (string)trans('firefly.customRange')) {
if ($label === (string) trans('firefly.everything') || $label === (string) trans('firefly.customRange')) {
$isCustomRange = true;
Log::debug('Range is now marked as "custom".');
}
@ -80,7 +82,7 @@ class HomeController extends Controller
$diff = $start->diffInDays($end) + 1;
if ($diff > 50) {
$request->session()->flash('warning', (string)trans('firefly.warning_much_data', ['days' => $diff]));
$request->session()->flash('warning', (string) trans('firefly.warning_much_data', ['days' => $diff]));
}
$request->session()->put('is_custom_range', $isCustomRange);
@ -112,7 +114,7 @@ class HomeController extends Controller
if (0 === $count) {
return redirect(route('new-user.index'));
}
$subTitle = (string)trans('firefly.welcome_back');
$subTitle = (string) trans('firefly.welcome_back');
$transactions = [];
$frontPage = app('preferences')->getFresh('frontPageAccounts', $repository->getAccountsByType([AccountType::ASSET])->pluck('id')->toArray());
/** @var Carbon $start */

View File

@ -427,9 +427,7 @@ class ProfileController extends Controller
// now actually update user:
$repository->changeEmail($user, $newEmail);
// call event.
$ipAddress = $request->ip();
event(new UserChangedEmail($user, $newEmail, $oldEmail, $ipAddress));
event(new UserChangedEmail($user, $newEmail, $oldEmail));
// force user logout.
Auth::guard()->logout();

View File

@ -37,21 +37,11 @@ class AccessTokenCreatedMail extends Mailable
use Queueable, SerializesModels;
/** @var string Email address of admin */
public $email;
/** @var string IP address of admin */
public $ipAddress;
/**
* AccessTokenCreatedMail constructor.
*
* @param string $email
* @param string $ipAddress
*/
public function __construct(string $email, string $ipAddress)
public function __construct()
{
$this->email = $email;
$this->ipAddress = $ipAddress;
}
/**
@ -61,7 +51,8 @@ class AccessTokenCreatedMail extends Mailable
*/
public function build(): self
{
return $this->view('emails.access-token-created-html')->text('emails.access-token-created-text')
->subject((string)trans('email.access_token_created_subject'));
return $this
->markdown('emails.token-created')
->subject((string)trans('email.access_token_created_subject'));
}
}

View File

@ -38,18 +38,15 @@ class AdminTestMail extends Mailable
use Queueable, SerializesModels;
public string $email;
public string $ipAddress;
/**
* ConfirmEmailChangeMail constructor.
*
* @param string $email
* @param string $ipAddress
*/
public function __construct(string $email, string $ipAddress)
public function __construct(string $email)
{
$this->email = $email;
$this->ipAddress = $ipAddress;
}
/**
@ -59,7 +56,8 @@ class AdminTestMail extends Mailable
*/
public function build(): self
{
return $this->view('emails.admin-test-html')->text('emails.admin-test-text')
->subject((string)trans('email.admin_test_subject'));
return $this
->markdown('emails.admin-test')
->subject((string)trans('email.admin_test_subject'));
}
}

View File

@ -14,7 +14,6 @@ class BillWarningMail extends Mailable
public Bill $bill;
public string $field;
public int $diff;
public string $ipAddress;
/**
* ConfirmEmailChangeMail constructor.
@ -22,14 +21,12 @@ class BillWarningMail extends Mailable
* @param Bill $bill
* @param string $field
* @param int $diff
* @param string $ipAddress
*/
public function __construct(Bill $bill, string $field, int $diff, string $ipAddress)
public function __construct(Bill $bill, string $field, int $diff)
{
$this->bill = $bill;
$this->field = $field;
$this->diff = $diff;
$this->ipAddress = $ipAddress;
}
/**
@ -45,8 +42,7 @@ class BillWarningMail extends Mailable
}
return $this
->view('emails.bill-warning-html')
->text('emails.bill-warning-text')
->markdown('emails.bill-warning')
->subject($subject);
}
}

View File

@ -37,25 +37,23 @@ class ConfirmEmailChangeMail extends Mailable
{
use Queueable, SerializesModels;
public string $ipAddress;
public string $newEmail;
public string $oldEmail;
public string $uri;
public string $url;
/**
* ConfirmEmailChangeMail constructor.
*
* @param string $newEmail
* @param string $oldEmail
* @param string $uri
* @param string $url
* @param string $ipAddress
*/
public function __construct(string $newEmail, string $oldEmail, string $uri, string $ipAddress)
public function __construct(string $newEmail, string $oldEmail, string $url)
{
$this->newEmail = $newEmail;
$this->oldEmail = $oldEmail;
$this->uri = $uri;
$this->ipAddress = $ipAddress;
$this->url = $url;
}
/**
@ -65,7 +63,10 @@ class ConfirmEmailChangeMail extends Mailable
*/
public function build(): self
{
return $this->view('emails.confirm-email-change-html')->text('emails.confirm-email-change-text')
->subject((string) trans('email.email_change_subject'));
return $this
//->view('emails.confirm-email-change-html')
//->text('emails.confirm-email-change-text')
->markdown('emails.confirm-email-change')
->subject((string) trans('email.email_change_subject'));
}
}

View File

@ -56,8 +56,6 @@ class NewIPAddressWarningMail extends Mailable
*/
public function build(): self
{
// time
$this->time = now(config('app.timezone'))->isoFormat((string)trans('config.date_time_js'));
$this->host = '';
try {
@ -69,7 +67,8 @@ class NewIPAddressWarningMail extends Mailable
$this->host = $hostName;
}
return $this->view('emails.new-ip-html')->text('emails.new-ip-text')
return $this
->markdown('emails.new-ip')
->subject((string)trans('email.login_from_new_ip'));
}
}

View File

@ -37,25 +37,16 @@ class OAuthTokenCreatedMail extends Mailable
{
use Queueable, SerializesModels;
/** @var Client The client */
public $client;
/** @var string Email address of admin */
public $email;
/** @var string IP address of admin */
public $ipAddress;
public Client $client;
/**
* OAuthTokenCreatedMail constructor.
*
* @param string $email
* @param string $ipAddress
* @param Client $client
*/
public function __construct(string $email, string $ipAddress, Client $client)
public function __construct(Client $client)
{
$this->email = $email;
$this->ipAddress = $ipAddress;
$this->client = $client;
$this->client = $client;
}
/**
@ -65,7 +56,8 @@ class OAuthTokenCreatedMail extends Mailable
*/
public function build(): self
{
return $this->view('emails.oauth-client-created-html')->text('emails.oauth-client-created-text')
->subject((string)trans('email.oauth_created_subject'));
return $this
->markdown('emails.oauth-client-created')
->subject((string) trans('email.oauth_created_subject'));
}
}

View File

@ -38,21 +38,16 @@ class RegisteredUser extends Mailable
{
use Queueable, SerializesModels;
/** @var string Email address of user */
public $address;
/** @var string IP address of user */
public $ipAddress;
public string $address;
/**
* Create a new message instance.
*
* @param string $address
* @param string $ipAddress
*/
public function __construct(string $address, string $ipAddress)
public function __construct(string $address)
{
$this->address = $address;
$this->ipAddress = $ipAddress;
}
/**
@ -62,6 +57,8 @@ class RegisteredUser extends Mailable
*/
public function build(): self
{
return $this->view('emails.registered-html')->text('emails.registered-text')->subject((string)trans('email.registered_subject'));
return $this
->markdown('emails.registered')
->subject((string)trans('email.registered_subject'));
}
}

View File

@ -40,23 +40,17 @@ class ReportNewJournalsMail extends Mailable
{
use Queueable, SerializesModels;
public string $email;
public Collection $groups;
public string $ipAddress;
public array $transformed;
/**
* ConfirmEmailChangeMail constructor.
*
* @param string $email
* @param string $ipAddress
* @param Collection $groups
*/
public function __construct(string $email, string $ipAddress, Collection $groups)
public function __construct(Collection $groups)
{
$this->email = $email;
$this->ipAddress = $ipAddress;
$this->groups = $groups;
$this->groups = $groups;
}
/**
@ -68,8 +62,9 @@ class ReportNewJournalsMail extends Mailable
{
$this->transform();
return $this->view('emails.report-new-journals-html')->text('emails.report-new-journals-text')
->subject((string)trans_choice('email.new_journals_subject', $this->groups->count()));
return $this
->markdown('emails.report-new-journals')
->subject((string) trans_choice('email.new_journals_subject', $this->groups->count()));
}
private function transform(): void

View File

@ -37,21 +37,16 @@ class RequestedNewPassword extends Mailable
{
use Queueable, SerializesModels;
/** @var string IP address of user */
public $ipAddress;
/** @var string URI of password change link */
public $url;
public string $url;
/**
* RequestedNewPassword constructor.
*
* @param string $url
* @param string $ipAddress
*/
public function __construct(string $url, string $ipAddress)
public function __construct(string $url)
{
$this->url = $url;
$this->ipAddress = $ipAddress;
}
/**
@ -61,6 +56,8 @@ class RequestedNewPassword extends Mailable
*/
public function build(): self
{
return $this->view('emails.password-html')->text('emails.password-text')->subject((string)trans('email.reset_pw_subject'));
return $this
->markdown('emails.password')
->subject((string)trans('email.reset_pw_subject'));
}
}

View File

@ -35,29 +35,22 @@ class UndoEmailChangeMail extends Mailable
{
use Queueable, SerializesModels;
/** @var string IP address of user */
public $ipAddress;
/** @var string New email address */
public $newEmail;
/** @var string Old email address */
public $oldEmail;
/** @var string URI to undo */
public $uri;
public string $newEmail;
public string $oldEmail;
public string $url;
/**
* UndoEmailChangeMail constructor.
*
* @param string $newEmail
* @param string $oldEmail
* @param string $uri
* @param string $ipAddress
* @param string $url
*/
public function __construct(string $newEmail, string $oldEmail, string $uri, string $ipAddress)
public function __construct(string $newEmail, string $oldEmail, string $url)
{
$this->newEmail = $newEmail;
$this->oldEmail = $oldEmail;
$this->uri = $uri;
$this->ipAddress = $ipAddress;
$this->url = $url;
}
/**
@ -67,7 +60,8 @@ class UndoEmailChangeMail extends Mailable
*/
public function build(): self
{
return $this->view('emails.undo-email-change-html')->text('emails.undo-email-change-text')
->subject((string)trans('email.email_change_subject'));
return $this
->markdown('emails.undo-email-change')
->subject((string)trans('email.email_change_subject'));
}
}

View File

@ -179,7 +179,6 @@ class EventServiceProvider extends ServiceProvider
}
$email = $user->email;
$ipAddress = Request::ip();
// see if user has alternative email address:
$pref = app('preferences')->getForUser($user, 'remote_guard_alt_email');
@ -187,10 +186,10 @@ class EventServiceProvider extends ServiceProvider
$email = $pref->data;
}
Log::debug(sprintf('Now in EventServiceProvider::registerCreateEvents. Email is %s, IP is %s', $email, $ipAddress));
Log::debug(sprintf('Now in EventServiceProvider::registerCreateEvents. Email is %s', $email));
try {
Log::debug('Trying to send message...');
Mail::to($email)->send(new OAuthTokenCreatedMail($email, $ipAddress, $oauthClient));
Mail::to($email)->send(new OAuthTokenCreatedMail($oauthClient));
} catch (Exception $e) { // @phpstan-ignore-line
Log::debug('Send message failed! :(');
Log::error($e->getMessage());

View File

@ -1,6 +1,9 @@
<?php
declare(strict_types=1);
use FireflyIII\Http\Middleware\EncryptCookies;
use FireflyIII\Http\Middleware\VerifyCsrfToken;
return [
/*
@ -55,8 +58,8 @@ return [
*/
'middleware' => [
'verify_csrf_token' => \FireflyIII\Http\Middleware\VerifyCsrfToken::class,
'encrypt_cookies' => \FireflyIII\Http\Middleware\EncryptCookies::class,
'verify_csrf_token' => VerifyCsrfToken::class,
'encrypt_cookies' => EncryptCookies::class,
],
];

View File

@ -24,97 +24,94 @@ declare(strict_types=1);
return [
// common items
'greeting' => 'Hi there,',
'closing' => 'Beep boop,',
'signature' => 'The Firefly III Mail Robot',
'footer_ps' => 'PS: This message was sent because a request from IP :ipAddress triggered it.',
'greeting' => 'Hi there,',
'closing' => 'Beep boop,',
'signature' => 'The Firefly III Mail Robot',
'footer_ps' => 'PS: This message was sent because a request from IP :ipAddress triggered it.',
// admin test
'admin_test_subject' => 'A test message from your Firefly III installation',
'admin_test_body' => 'This is a test message from your Firefly III instance. It was sent to :email.',
'admin_test_subject' => 'A test message from your Firefly III installation',
'admin_test_body' => 'This is a test message from your Firefly III instance. It was sent to :email.',
// new IP
'login_from_new_ip' => 'New login on Firefly III',
'new_ip_body' => 'Firefly III detected a new login on your account from an unknown IP address. If you never logged in from the IP address below, or it has been more than six months ago, Firefly III will warn you.',
'new_ip_warning' => 'If you recognize this IP address or the login, you can ignore this message. If you didn\'t login, of if you have no idea what this is about, verify your password security, change it, and log out all other sessions. To do this, go to your profile page. Of course you have 2FA enabled already, right? Stay safe!',
'ip_address' => 'IP address',
'host_name' => 'Host',
'date_time' => 'Date + time',
'login_from_new_ip' => 'New login on Firefly III',
'new_ip_body' => 'Firefly III detected a new login on your account from an unknown IP address. If you never logged in from the IP address below, or it has been more than six months ago, Firefly III will warn you.',
'new_ip_warning' => 'If you recognize this IP address or the login, you can ignore this message. If you didn\'t login, of if you have no idea what this is about, verify your password security, change it, and log out all other sessions. To do this, go to your profile page. Of course you have 2FA enabled already, right? Stay safe!',
'ip_address' => 'IP address',
'host_name' => 'Host',
'date_time' => 'Date + time',
// access token created
'access_token_created_subject' => 'A new access token was created',
'access_token_created_body' => 'Somebody (hopefully you) just created a new Firefly III API Access Token for your user account.',
'access_token_created_explanation' => 'With this token, they can access <strong>all</strong> of your financial records through the Firefly III API.',
'access_token_created_revoke' => 'If this wasn\'t you, please revoke this token as soon as possible at :url.',
'access_token_created_subject' => 'A new access token was created',
'access_token_created_body' => 'Somebody (hopefully you) just created a new Firefly III API Access Token for your user account.',
'access_token_created_explanation' => 'With this token, they can access **all** of your financial records through the Firefly III API.',
'access_token_created_revoke' => 'If this wasn\'t you, please revoke this token as soon as possible at :url',
// registered
'registered_subject' => 'Welcome to Firefly III!',
'registered_welcome' => 'Welcome to <a style="color:#337ab7" href=":address">Firefly III</a>. Your registration has made it, and this email is here to confirm it. Yay!',
'registered_pw' => 'If you have forgotten your password already, please reset it using <a style="color:#337ab7" href=":address/password/reset">the password reset tool</a>.',
'registered_help' => 'There is a help-icon in the top right corner of each page. If you need help, click it!',
'registered_doc_html' => 'If you haven\'t already, please read the <a style="color:#337ab7" href="https://docs.firefly-iii.org/about-firefly-iii/personal-finances">grand theory</a>.',
'registered_doc_text' => 'If you haven\'t already, please read the first use guide and the full description.',
'registered_closing' => 'Enjoy!',
'registered_firefly_iii_link' => 'Firefly III:',
'registered_pw_reset_link' => 'Password reset:',
'registered_doc_link' => 'Documentation:',
'registered_subject' => 'Welcome to Firefly III!',
'registered_welcome' => 'Welcome to [Firefly III](:address). Your registration has made it, and this email is here to confirm it. Yay!',
'registered_pw' => 'If you have forgotten your password already, please reset it using [the password reset tool](:address/password/reset).',
'registered_help' => 'There is a help-icon in the top right corner of each page. If you need help, click it!',
'registered_doc_html' => 'If you haven\'t already, please read the [grand theory](https://docs.firefly-iii.org/about-firefly-iii/personal-finances).',
'registered_doc_text' => 'If you haven\'t already, please also read the first use guide and the full description.',
'registered_closing' => 'Enjoy!',
'registered_firefly_iii_link' => 'Firefly III:',
'registered_pw_reset_link' => 'Password reset:',
'registered_doc_link' => 'Documentation:',
// email change
'email_change_subject' => 'Your Firefly III email address has changed',
'email_change_body_to_new' => 'You or somebody with access to your Firefly III account has changed your email address. If you did not expect this message, please ignore and delete it.',
'email_change_body_to_old' => 'You or somebody with access to your Firefly III account has changed your email address. If you did not expect this to happen, you <strong>must</strong> follow the "undo"-link below to protect your account!',
'email_change_ignore' => 'If you initiated this change, you may safely ignore this message.',
'email_change_old' => 'The old email address was: :email',
'email_change_old_strong' => 'The old email address was: <strong>:email</strong>',
'email_change_new' => 'The new email address is: :email',
'email_change_new_strong' => 'The new email address is: <strong>:email</strong>',
'email_change_instructions' => 'You cannot use Firefly III until you confirm this change. Please follow the link below to do so.',
'email_change_undo_link' => 'To undo the change, follow this link:',
'email_change_subject' => 'Your Firefly III email address has changed',
'email_change_body_to_new' => 'You or somebody with access to your Firefly III account has changed your email address. If you did not expect this message, please ignore and delete it.',
'email_change_body_to_old' => 'You or somebody with access to your Firefly III account has changed your email address. If you did not expect this to happen, you **must** follow the "undo"-link below to protect your account!',
'email_change_ignore' => 'If you initiated this change, you may safely ignore this message.',
'email_change_old' => 'The old email address was: :email',
'email_change_old_strong' => 'The old email address was: **:email**',
'email_change_new' => 'The new email address is: :email',
'email_change_new_strong' => 'The new email address is: **:email**',
'email_change_instructions' => 'You cannot use Firefly III until you confirm this change. Please follow the link below to do so.',
'email_change_undo_link' => 'To undo the change, follow this link:',
// OAuth token created
'oauth_created_subject' => 'A new OAuth client has been created',
'oauth_created_body' => 'Somebody (hopefully you) just created a new Firefly III API OAuth Client for your user account. It\'s labeled ":name" and has callback URL <span style="font-family: monospace;">:url</span>.',
'oauth_created_explanation' => 'With this client, they can access <strong>all</strong> of your financial records through the Firefly III API.',
'oauth_created_undo' => 'If this wasn\'t you, please revoke this client as soon as possible at :url.',
'oauth_created_subject' => 'A new OAuth client has been created',
'oauth_created_body' => 'Somebody (hopefully you) just created a new Firefly III API OAuth Client for your user account. It\'s labeled ":name" and has callback URL `:url`.',
'oauth_created_explanation' => 'With this client, they can access **all** of your financial records through the Firefly III API.',
'oauth_created_undo' => 'If this wasn\'t you, please revoke this client as soon as possible at `:url`',
// reset password
'reset_pw_subject' => 'Your password reset request',
'reset_pw_instructions' => 'Somebody tried to reset your password. If it was you, please follow the link below to do so.',
'reset_pw_warning' => '<strong>PLEASE</strong> verify that the link actually goes to the Firefly III you expect it to go!',
'reset_pw_subject' => 'Your password reset request',
'reset_pw_instructions' => 'Somebody tried to reset your password. If it was you, please follow the link below to do so.',
'reset_pw_warning' => '**PLEASE** verify that the link actually goes to the Firefly III you expect it to go!',
// error
'error_subject' => 'Caught an error in Firefly III',
'error_intro' => 'Firefly III v:version ran into an error: <span style="font-family: monospace;">:errorMessage</span>.',
'error_type' => 'The error was of type ":class".',
'error_timestamp' => 'The error occurred on/at: :time.',
'error_location' => 'This error occurred in file "<span style="font-family: monospace;">:file</span>" on line :line with code :code.',
'error_user' => 'The error was encountered by user #:id, <a href="mailto::email">:email</a>.',
'error_no_user' => 'There was no user logged in for this error or no user was detected.',
'error_ip' => 'The IP address related to this error is: :ip',
'error_url' => 'URL is: :url',
'error_user_agent' => 'User agent: :userAgent',
'error_stacktrace' => 'The full stacktrace is below. If you think this is a bug in Firefly III, you can forward this message to <a href="mailto:james@firefly-iii.org?subject=BUG!">james@firefly-iii.org</a>. This can help fix the bug you just encountered.',
'error_github_html' => 'If you prefer, you can also open a new issue on <a href="https://github.com/firefly-iii/firefly-iii/issues">GitHub</a>.',
'error_github_text' => 'If you prefer, you can also open a new issue on https://github.com/firefly-iii/firefly-iii/issues.',
'error_stacktrace_below' => 'The full stacktrace is below:',
'error_headers' => 'The following headers may also be relevant:',
'error_subject' => 'Caught an error in Firefly III',
'error_intro' => 'Firefly III v:version ran into an error: <span style="font-family: monospace;">:errorMessage</span>.',
'error_type' => 'The error was of type ":class".',
'error_timestamp' => 'The error occurred on/at: :time.',
'error_location' => 'This error occurred in file "<span style="font-family: monospace;">:file</span>" on line :line with code :code.',
'error_user' => 'The error was encountered by user #:id, <a href="mailto::email">:email</a>.',
'error_no_user' => 'There was no user logged in for this error or no user was detected.',
'error_ip' => 'The IP address related to this error is: :ip',
'error_url' => 'URL is: :url',
'error_user_agent' => 'User agent: :userAgent',
'error_stacktrace' => 'The full stacktrace is below. If you think this is a bug in Firefly III, you can forward this message to <a href="mailto:james@firefly-iii.org?subject=BUG!">james@firefly-iii.org</a>. This can help fix the bug you just encountered.',
'error_github_html' => 'If you prefer, you can also open a new issue on <a href="https://github.com/firefly-iii/firefly-iii/issues">GitHub</a>.',
'error_github_text' => 'If you prefer, you can also open a new issue on https://github.com/firefly-iii/firefly-iii/issues.',
'error_stacktrace_below' => 'The full stacktrace is below:',
'error_headers' => 'The following headers may also be relevant:',
// report new journals
'new_journals_subject' => 'Firefly III has created a new transaction|Firefly III has created :count new transactions',
'new_journals_header' => 'Firefly III has created a transaction for you. You can find it in your Firefly III installation:|Firefly III has created :count transactions for you. You can find them in your Firefly III installation:',
'new_journals_subject' => 'Firefly III has created a new transaction|Firefly III has created :count new transactions',
'new_journals_header' => 'Firefly III has created a transaction for you. You can find it in your Firefly III installation:|Firefly III has created :count transactions for you. You can find them in your Firefly III installation:',
// bill warning
'bill_warning_subject_end_date' => 'Your bill ":name" is due to end in :diff days',
'bill_warning_subject_now_end_date' => 'Your bill ":name" is due to end TODAY',
'bill_warning_subject_extension_date' => 'Your bill ":name" is due to be extended or cancelled in :diff days',
'bill_warning_subject_now_extension_date' => 'Your bill ":name" is due to be extended or cancelled TODAY',
'bill_warning_end_date_text' => 'Your bill ":name" is due to end on :date. This moment will pass in about :diff days.',
'bill_warning_extension_date_text' => 'Your bill ":name" is due to be extended or cancelled on :date. This moment will pass in about :diff days.',
'bill_warning_end_date_text_zero' => 'Your bill ":name" is due to end on :date. This moment will pass TODAY!',
'bill_warning_extension_date_text_zero' => 'Your bill ":name" is due to be extended or cancelled on :date. This moment will pass TODAY!',
'bill_warning_please_action' => 'Please take the appropriate action.',
'bill_warning_end_date_html' => 'Your bill <strong>":name"</strong> is due to end on :date. This moment will pass in about <strong>:diff days</strong>.',
'bill_warning_extension_date_html' => 'Your bill <strong>":name"</strong> is due to be extended or cancelled on :date. This moment will pass in about <strong>:diff days</strong>.',
'bill_warning_end_date_html_zero' => 'Your bill <strong>":name"</strong> is due to end on :date. This moment will pass <strong>TODAY!</strong>',
'bill_warning_extension_date_html_zero' => 'Your bill <strong>":name"</strong> is due to be extended or cancelled on :date. This moment will pass <strong>TODAY!</strong>',
'bill_warning_subject_end_date' => 'Your bill ":name" is due to end in :diff days',
'bill_warning_subject_now_end_date' => 'Your bill ":name" is due to end TODAY',
'bill_warning_subject_extension_date' => 'Your bill ":name" is due to be extended or cancelled in :diff days',
'bill_warning_subject_now_extension_date' => 'Your bill ":name" is due to be extended or cancelled TODAY',
'bill_warning_end_date' => 'Your bill **":name"** is due to end on :date. This moment will pass in about **:diff days**.',
'bill_warning_extension_date' => 'Your bill **":name"** is due to be extended or cancelled on :date. This moment will pass in about **:diff days**.',
'bill_warning_end_date_zero' => 'Your bill **":name"** is due to end on :date. This moment will pass **TODAY!**',
'bill_warning_extension_date_zero' => 'Your bill **":name"** is due to be extended or cancelled on :date. This moment will pass **TODAY!**',
'bill_warning_please_action' => 'Please take the appropriate action.',
];

View File

@ -1,13 +0,0 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.access_token_created_body') }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.access_token_created_explanation')|raw }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.access_token_created_revoke', {url: route('profile.index') }) }}
</p>
{% include 'emails.footer-html' %}

View File

@ -1,7 +0,0 @@
{% include 'emails.header-text' %}
{{ trans('email.access_token_created_body')|raw }}
{{ trans('email.access_token_created_explanation')|striptags|raw }}
{{ trans('email.access_token_created_revoke', {url: route('profile.index') })|raw }}
{% include 'emails.footer-text' %}

View File

@ -1,5 +0,0 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.admin_test_body', {email: email })}}
</p>
{% include 'emails.footer-html' %}

View File

@ -1,3 +0,0 @@
{% include 'emails.header-text' %}
{{ trans('email.admin_test_body', {email: email })|raw }}
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,3 @@
@component('mail::message')
{{ trans('email.admin_test_body', ['email' => $email]) }}
@endcomponent

View File

@ -1,25 +0,0 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{% if field == 'end_date' and diff != 0 %}
{{ trans('email.bill_warning_end_date_html', {name: bill.name|escape, date: bill.end_date.isoFormat(trans('config.month_and_day_js')), diff: diff})|raw }}
{% endif %}
{% if field == 'extension_date' and diff != 0 %}
{{ trans('email.bill_warning_extension_date_html', {name: bill.name|escape, date: bill.extension_date.isoFormat(trans('config.month_and_day_js')), diff: diff})|raw }}
{% endif %}
{% if field == 'end_date' and diff == 0 %}
{{ trans('email.bill_warning_end_date_html_zero', {name: bill.name|escape, date: bill.end_date.isoFormat(trans('config.month_and_day_js'))})|raw }}
{% endif %}
{% if field == 'extension_date' and diff == 0 %}
{{ trans('email.bill_warning_extension_date_html_zero', {name: bill.name|escape, date: bill.end_date.isoFormat(trans('config.month_and_day_js'))})|raw }}
{% endif %}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
Please take the appropriate action.
</p>
{% include 'emails.footer-html' %}

View File

@ -1,17 +0,0 @@
{% include 'emails.header-text' %}
{% if field == 'end_date' and diff != 0 %}
{{ trans('email.bill_warning_end_date_text', {name: bill.name, date: bill.end_date.isoFormat(trans('config.month_and_day_js')), diff: diff})|raw }}
{% endif %}
{% if field == 'extension_date' and diff != 0 %}
{{ trans('email.bill_warning_extension_date_text', {name: bill.name|escape, date: bill.extension_date.isoFormat(trans('config.month_and_day_js')), diff: diff})|raw }}
{% endif %}
{% if field == 'end_date' and diff == 0 %}
{{ trans('email.bill_warning_end_date_text_zero', {name: bill.name|escape, date: bill.end_date.isoFormat(trans('config.month_and_day_js'))})|raw }}
{% endif %}
{% if field == 'extension_date' and diff == 0 %}
{{ trans('email.bill_warning_extension_date_text_zero', {name: bill.name|escape, date: bill.end_date.isoFormat(trans('config.month_and_day_js'))})|raw }}
{% endif %}
{{ trans('email.bill_warning_please_action') }}
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,20 @@
@component('mail::message')
@if($field === 'end_date' && $diff !== 0)
{{ trans('email.bill_warning_end_date', ['name' => $bill->name, 'date' => $bill->end_date->isoFormat(trans('config.month_and_day_js')), 'diff' => $diff]) }}
@endif
@if($field === 'extension_date' && $diff !== 0)
{{ trans('email.bill_warning_extension_date', ['name' => $bill->name, 'date' => $bill->end_date->isoFormat(trans('config.month_and_day_js')), 'diff' => $diff]) }}
@endif
@if($field === 'end_date' && $diff === 0)
{{ trans('email.bill_warning_end_date_zero', ['name' => $bill->name, 'date' => $bill->end_date->isoFormat(trans('config.month_and_day_js')) ]) }}
@endif
@if($field === 'extension_date' && $diff === 0)
{{ trans('email.bill_warning_extension_date_zero', ['name' => $bill->name, 'date' => $bill->end_date->isoFormat(trans('config.month_and_day_js')) ]) }}
@endif
{{ trans('email.bill_warning_please_action') }}
@endcomponent

View File

@ -1,18 +0,0 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.email_change_body_to_new')}}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{trans('email.email_change_old', { email: oldEmail }) }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{trans('email.email_change_new_strong', { email: newEmail })|raw }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.email_change_instructions')}}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
<a href="{{ uri }}">{{ uri }}</a>
</p>
{% include 'emails.footer-html' %}

View File

@ -1,10 +0,0 @@
{% include 'emails.header-text' %}
{{ trans('email.email_change_body_to_new')|raw }}
{{trans('email.email_change_old', { email: oldEmail })|raw }}
{{trans('email.email_change_new', { email: newEmail })|raw }}
{{ trans('email.email_change_instructions')|raw }}
{{ uri }}
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,11 @@
@component('mail::message')
{{ trans('email.email_change_body_to_new') }}
{{trans('email.email_change_old', ['email' => $oldEmail]) }}
{{trans('email.email_change_new', ['email' => $newEmail]) }}
{{ trans('email.email_change_instructions') }}
{{ $url }}
@endcomponent

View File

@ -1,16 +0,0 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.new_ip_body') }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.ip_address') }}: <strong>{{ ipAddress }}</strong><br />
{% if ''!= host %}{{ trans('email.host_name') }}: {{ host }}<br />{% endif %}
{{ trans('email.date_time') }}: {{ time }}<br />
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.new_ip_warning') }}
</p>
{% include 'emails.footer-html' %}

View File

@ -1,10 +0,0 @@
{% include 'emails.header-text' %}
{{ trans('email.new_ip_body') }}
{{ trans('email.ip_address') }}: {{ ipAddress }}
{% if ''!= host %}{{ trans('email.host_name') }}: {{ host }}{% endif %}
{{ trans('email.date_time') }}: {{ time }}
{{ trans('email.new_ip_warning') }}
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,13 @@
@component('mail::message')
{{ trans('email.new_ip_body') }}
{{ trans('email.ip_address') }}: {{ $ipAddress }}
@if('' !== $host)
{{ trans('email.host_name') }}: {{ $host }}
@endif
{{ trans('email.date_time') }}: {{ $time }}
{{ trans('email.new_ip_warning') }}
@endcomponent

View File

@ -1,13 +0,0 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.oauth_created_body', { name:client.name, url: client.redirect })|raw }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.oauth_created_explanation')|raw }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.oauth_created_undo', { url:route('profile.index')}) }}
</p>
{% include 'emails.footer-html' %}

View File

@ -1,7 +0,0 @@
{% include 'emails.header-text' %}
{{ trans('email.oauth_created_body', {name: client.name, url: client.redirect })|striptags|raw }}
{{ trans('email.oauth_created_explanation')|striptags|raw }}
{{ trans('email.oauth_created_undo', { url:route('profile.index')})|raw }}
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,8 @@
@component('mail::message')
{{ trans('email.oauth_created_body', ['name' => $client->name, 'url' => $client->redirect]) }}
{{ trans('email.oauth_created_explanation') }}
{{ trans('email.oauth_created_undo', ['url' => route('profile.index')] ) }}
@endcomponent

View File

@ -1,13 +0,0 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.reset_pw_instructions') }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.reset_pw_warning')|raw }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
<a href="{{ url }}">{{ url }}</a>
</p>
{% include 'emails.footer-html' %}

View File

@ -1,7 +0,0 @@
{% include 'emails.header-text' %}
{{ trans('email.reset_pw_instructions')|raw }}
{{ trans('email.reset_pw_warning')|striptags|raw }}
{{ url }}
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,8 @@
@component('mail::message')
{{ trans('email.reset_pw_instructions') }}
{{ trans('email.reset_pw_warning') }}
{{ $url }}
@endcomponent

View File

@ -1,20 +0,0 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.registered_welcome', {address: address})|raw }}
</p>
<ul>
<li style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.registered_pw', {address: address})|raw }}
</li>
<li style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.registered_help')}}
</li>
<li style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.registered_doc_html')|raw }}
</li>
</ul>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.registered_closing')}}
</p>
{% include 'emails.footer-html' %}

View File

@ -1,21 +0,0 @@
{% include 'emails.header-text' %}
{{ trans('email.registered_welcome')|striptags|raw }}
* {{ trans('email.registered_pw')|striptags|raw }}
* {{ trans('email.registered_help')|raw }}
* {{ trans('email.registered_doc_text')|raw }}
{{ trans('email.registered_closing')|raw }}
{{ trans('email.registered_firefly_iii_link')|raw }}
{{ address }}
{{ trans('email.registered_pw_reset_link')|raw }}
{{ address }}/password/reset
{{ trans('email.registered_doc_link')}}
https://github.com/firefly-iii/firefly-iii
https://firefly-iii.org/
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,14 @@
@component('mail::message')
{{ trans('email.registered_welcome') }}
* {{ trans('email.registered_pw', ['address' => $address]) }}
* {{ trans('email.registered_help') }}
* {{ trans('email.registered_doc_text') }}
{{ trans('email.registered_closing') }}
* {{ trans('email.registered_firefly_iii_link')}} [{{$address }}]({{$address }})
* {{ trans('email.registered_pw_reset_link')}} [{{ $address }}/password/reset]({{ $address }}/password/reset)
* {{ trans('email.registered_doc_link')}} [https://docs.firefly-iii.org/](https://docs.firefly-iii.org/)
@endcomponent

View File

@ -1,78 +0,0 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans_choice('email.new_journals_header', transformed|length ) }}
</p>
<!-- loop groups -->
<ol>
{% for group in transformed %}
<li>
{% set count = group.transactions|length %}
<!-- if journals === 1, skip straight to journals. -->
{% if 1 == count %}
{% set journal = group.transactions[0] %}
<a href="{{ route('transactions.show', [group.id]) }}">{{ journal.description }}</a>,
<!-- amount -->
{% if journal.type == 'deposit' %}
<span style="color:#3c763d;">
{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}
{% if null != journal.foreign_amount*-1 %}
({{ formatAmountBySymbol(journal.foreign_amount|floatval, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
{% endif %}
</span>
{% elseif journal.type == 'transfer' %}
<span style="color:#31708f">
{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}
{% if null != journal.foreign_amount %}
({{ formatAmountBySymbol((journal.foreign_amount*-1)|floatval, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
{% endif %}
</span>
{% else %}
<span style="color:#a94442">
{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}
{% if null != journal.foreign_amount %}
({{ formatAmountBySymbol((journal.foreign_amount*-1)|floatval, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
{% endif %}
</span>
{% endif %}
<!-- / amount -->
{% else %}
<a href="{{ route('transactions.show', [group.id]) }}">{{ group.group_title }}</a>
<ol>
{% for journal in group.transactions %}
<li>
{{ journal.description }},
<!-- amount -->
{% if journal.type == 'deposit' %}
<span style="color:#3c763d;">
{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}
{% if null != journal.foreign_amount*-1 %}
({{ formatAmountBySymbol(journal.foreign_amount|floatval, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
{% endif %}
</span>
{% elseif journal.type == 'transfer' %}
<span style="color:#31708f">
{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}
{% if null != journal.foreign_amount %}
({{ formatAmountBySymbol((journal.foreign_amount*-1)|floatval, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
{% endif %}
</span>
{% else %}
<span style="color:#a94442">
{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}
{% if null != journal.foreign_amount %}
({{ formatAmountBySymbol((journal.foreign_amount*-1)|floatval, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }})
{% endif %}
</span>
{% endif %}
</li>
{% endfor %}
</ol>
{% endif %}
</li>
{% endfor %}
</ol>
{% include 'emails.footer-html' %}

View File

@ -1,16 +0,0 @@
{% include 'emails.header-text' %}
{{ trans_choice('email.new_journals_header', transformed|length )|raw }}
{% for group in transformed %}
{% set count = group.transactions|length %}
{% if 1 == count %}{% set journal = group.transactions[0] %}
- {{ journal.description }}, {% if journal.type == 'deposit' %}{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}{% if null != journal.foreign_amount*-1 %} ({{ formatAmountBySymbol(journal.foreign_amount|floatval, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }}){% endif %}{% elseif journal.type == 'transfer' %}{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}{% if null != journal.foreign_amount %} ({{ formatAmountBySymbol(journal.foreign_amount|floatval*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }}){% endif %}{% else %}{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}{% if null != journal.foreign_amount %} ({{ formatAmountBySymbol(journal.foreign_amount|floatval*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }}){% endif %}{% endif %}
{% else %}- {{ group.group_title }}
{% for journal in group.transactions %}-- {{ journal.description }}, {% if journal.type == 'deposit' %}{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}{% if null != journal.foreign_amount*-1 %} ({{ formatAmountBySymbol(journal.foreign_amount|floatval, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }}){% endif %}{% elseif journal.type == 'transfer' %}{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}{% if null != journal.foreign_amount %} ({{ formatAmountBySymbol(journal.foreign_amount|floatval*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }}){% endif %}{% else %}{{ formatAmountBySymbol((journal.amount*-1)|floatval, journal.currency_symbol, journal.currency_decimal_places, false) }}{% if null != journal.foreign_amount %} ({{ formatAmountBySymbol(journal.foreign_amount|floatval*-1, journal.foreign_currency_symbol, journal.foreign_currency_decimal_places, false) }}){% endif %}{% endif %}
{% endfor %}
{% endif %}
{% endfor %}
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,17 @@
@component('mail::message')
{{ trans_choice('email.new_journals_header', count($transformed)) }}
@foreach($transformed as $group)
- [{{ $group['transactions'][0]['description'] }}]({{ route('transactions.show', [$group['id']]) }})
@if('withdrawal' === $group['transactions'][0]['type'])
{{ $group['transactions'][0]['currency_code']}} {{ round((float)bcmul($group['transactions'][0]['amount'], '-1'), $group['transactions'][0]['currency_decimal_places']) }}
@endif
@if('deposit' === $group['transactions'][0]['type'])
{{ $group['transactions'][0]['currency_code']}} {{ round((float)bcmul($group['transactions'][0]['amount'], '1'), $group['transactions'][0]['currency_decimal_places']) }}
@endif
@if('transfer' === $group['transactions'][0]['type'])
{{ $group['transactions'][0]['currency_code']}} {{ round((float)bcmul($group['transactions'][0]['amount'], '1'), $group['transactions'][0]['currency_decimal_places']) }}
@endif
@endforeach
@endcomponent

View File

@ -0,0 +1,7 @@
@component('mail::message')
{{ trans('email.access_token_created_body') }}
{{ trans('email.access_token_created_explanation') }}
{{ trans('email.access_token_created_revoke', ['url' => route('profile.index')]) }}
@endcomponent

View File

@ -1,17 +0,0 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.email_change_body_to_old')|raw }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{ trans('email.email_change_ignore') }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{trans('email.email_change_old_strong', { email: oldEmail })|raw }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{trans('email.email_change_new', { email: newEmail }) }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
{{trans('email.email_change_undo_link')}} <a href="{{ uri }}">{{ uri }}</a>
</p>
{% include 'emails.footer-html' %}

View File

@ -1,11 +0,0 @@
{% include 'emails.header-text' %}
{{ trans('email.email_change_body_to_old')|striptags|raw }}
{{ trans('email.email_change_ignore')|raw }}
{{trans('email.email_change_old', { email: oldEmail })|raw }}
{{trans('email.email_change_new', { email: newEmail })|raw }}
{{ trans('email.email_change_undo_link')|raw }} {{ uri }}
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,11 @@
@component('mail::message')
{{ trans('email.email_change_body_to_old') }}
{{ trans('email.email_change_ignore')}}
{{trans('email.email_change_old', ['email' => $oldEmail]) }}
{{trans('email.email_change_new', ['email' => $newEmail]) }}
{{ trans('email.email_change_undo_link') }} {{ $url }}
@endcomponent

View File

@ -0,0 +1,19 @@
<table class="action" align="center" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table width="100%" border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table border="0" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
<a href="{{ $url }}" class="button button-{{ $color ?? 'primary' }}" target="_blank" rel="noopener">{{ $slot }}</a>
</td>
</tr>
</table>
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,11 @@
<tr>
<td>
<table class="footer" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="content-cell" align="center">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>

View File

@ -0,0 +1,7 @@
<tr>
<td class="header">
<a href="#" style="display: inline-block;">
Firefly III
</a>
</td>
</tr>

View File

@ -0,0 +1,56 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="color-scheme" content="light">
<meta name="supported-color-schemes" content="light">
<style>
@media only screen and (max-width: 600px) {
.inner-body {
width: 100% !important;
}
.footer {
width: 100% !important;
}
}
@media only screen and (max-width: 500px) {
.button {
width: 100% !important;
}
}
</style>
</head>
<body>
<table class="wrapper" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td align="center">
<table class="content" width="100%" cellpadding="0" cellspacing="0" role="presentation">
{{ $header ?? '' }}
<!-- Email Body -->
<tr>
<td class="body" width="100%" cellpadding="0" cellspacing="0">
<table class="inner-body" align="center" width="570" cellpadding="0" cellspacing="0" role="presentation">
<!-- Body content -->
<tr>
<td class="content-cell">
{{ Illuminate\Mail\Markdown::parse($slot) }}
{{ $subcopy ?? '' }}
</td>
</tr>
</table>
</td>
</tr>
{{ $footer ?? '' }}
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -0,0 +1,33 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
{{ config('app.name') }}
@endcomponent
@endslot
{{-- Body --}}
{{ trans('email.greeting') }}
{{ $slot }}
{{ trans('email.closing') }}
{{ trans('email.signature')}}
{{-- Subcopy --}}
@isset($subcopy)
@slot('subcopy')
@component('mail::subcopy')
{{ $subcopy }}
@endcomponent
@endslot
@endisset
{{-- Footer --}}
@slot('footer')
@component('mail::footer')
{{ trans('email.footer_ps', ['ipAddress' => request()?->ip() ?? '']) }}
@endcomponent
@endslot
@endcomponent

View File

@ -0,0 +1,14 @@
<table class="panel" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-content">
<table width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td class="panel-item">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>
</td>
</tr>
</table>

View File

@ -0,0 +1,7 @@
<table class="subcopy" width="100%" cellpadding="0" cellspacing="0" role="presentation">
<tr>
<td>
{{ Illuminate\Mail\Markdown::parse($slot) }}
</td>
</tr>
</table>

View File

@ -0,0 +1,3 @@
<div class="table">
{{ Illuminate\Mail\Markdown::parse($slot) }}
</div>

View File

@ -0,0 +1,290 @@
/* Base */
body,
body *:not(html):not(style):not(br):not(tr):not(code) {
box-sizing: border-box;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif,
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
position: relative;
}
body {
-webkit-text-size-adjust: none;
background-color: #ffffff;
color: #718096;
height: 100%;
line-height: 1.4;
margin: 0;
padding: 0;
width: 100% !important;
}
p,
ul,
ol,
blockquote {
line-height: 1.4;
text-align: left;
}
a {
color: #3869d4;
}
a img {
border: none;
}
/* Typography */
h1 {
color: #3d4852;
font-size: 18px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h2 {
font-size: 16px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
h3 {
font-size: 14px;
font-weight: bold;
margin-top: 0;
text-align: left;
}
p {
font-size: 16px;
line-height: 1.5em;
margin-top: 0;
text-align: left;
}
p.sub {
font-size: 12px;
}
img {
max-width: 100%;
}
/* Layout */
.wrapper {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #edf2f7;
margin: 0;
padding: 0;
width: 100%;
}
.content {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 0;
padding: 0;
width: 100%;
}
/* Header */
.header {
padding: 25px 0;
text-align: center;
}
.header a {
color: #3d4852;
font-size: 19px;
font-weight: bold;
text-decoration: none;
}
/* Logo */
.logo {
height: 75px;
max-height: 75px;
width: 75px;
}
/* Body */
.body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
background-color: #edf2f7;
border-bottom: 1px solid #edf2f7;
border-top: 1px solid #edf2f7;
margin: 0;
padding: 0;
width: 100%;
}
.inner-body {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
background-color: #ffffff;
border-color: #e8e5ef;
border-radius: 2px;
border-width: 1px;
box-shadow: 0 2px 0 rgba(0, 0, 150, 0.025), 2px 4px 0 rgba(0, 0, 150, 0.015);
margin: 0 auto;
padding: 0;
width: 570px;
}
/* Subcopy */
.subcopy {
border-top: 1px solid #e8e5ef;
margin-top: 25px;
padding-top: 25px;
}
.subcopy p {
font-size: 14px;
}
/* Footer */
.footer {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 570px;
margin: 0 auto;
padding: 0;
text-align: center;
width: 570px;
}
.footer p {
color: #b0adc5;
font-size: 12px;
text-align: center;
}
.footer a {
color: #b0adc5;
text-decoration: underline;
}
/* Tables */
.table table {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
width: 100%;
}
.table th {
border-bottom: 1px solid #edeff2;
margin: 0;
padding-bottom: 8px;
}
.table td {
color: #74787e;
font-size: 15px;
line-height: 18px;
margin: 0;
padding: 10px 0;
}
.content-cell {
max-width: 100vw;
padding: 32px;
}
/* Buttons */
.action {
-premailer-cellpadding: 0;
-premailer-cellspacing: 0;
-premailer-width: 100%;
margin: 30px auto;
padding: 0;
text-align: center;
width: 100%;
}
.button {
-webkit-text-size-adjust: none;
border-radius: 4px;
color: #fff;
display: inline-block;
overflow: hidden;
text-decoration: none;
}
.button-blue,
.button-primary {
background-color: #2d3748;
border-bottom: 8px solid #2d3748;
border-left: 18px solid #2d3748;
border-right: 18px solid #2d3748;
border-top: 8px solid #2d3748;
}
.button-green,
.button-success {
background-color: #48bb78;
border-bottom: 8px solid #48bb78;
border-left: 18px solid #48bb78;
border-right: 18px solid #48bb78;
border-top: 8px solid #48bb78;
}
.button-red,
.button-error {
background-color: #e53e3e;
border-bottom: 8px solid #e53e3e;
border-left: 18px solid #e53e3e;
border-right: 18px solid #e53e3e;
border-top: 8px solid #e53e3e;
}
/* Panels */
.panel {
border-left: #2d3748 solid 4px;
margin: 21px 0;
}
.panel-content {
background-color: #edf2f7;
color: #718096;
padding: 16px;
}
.panel-content p {
color: #718096;
}
.panel-item {
padding: 0;
}
.panel-item p:last-of-type {
margin-bottom: 0;
padding-bottom: 0;
}
/* Utilities */
.break-all {
word-break: break-all;
}

View File

@ -0,0 +1 @@
{{ $slot }}: {{ $url }}

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -0,0 +1 @@
[{{ $slot }}]({{ $url }})

View File

@ -0,0 +1,9 @@
{!! strip_tags($header) !!}
{!! strip_tags($slot) !!}
@isset($subcopy)
{!! strip_tags($subcopy) !!}
@endisset
{!! strip_tags($footer) !!}

View File

@ -0,0 +1,27 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
{{ config('app.name') }}
@endcomponent
@endslot
{{-- Body --}}
{{ $slot }}
{{-- Subcopy --}}
@isset($subcopy)
@slot('subcopy')
@component('mail::subcopy')
{{ $subcopy }}
@endcomponent
@endslot
@endisset
{{-- Footer --}}
@slot('footer')
@component('mail::footer')
© {{ date('Y') }} {{ config('app.name') }}. @lang('All rights reserved.')
@endcomponent
@endslot
@endcomponent

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -0,0 +1 @@
{{ $slot }}

View File

@ -0,0 +1 @@
{{ $slot }}