New code for email address change in profile. See #857

This commit is contained in:
James Cole 2017-09-26 08:52:16 +02:00
parent ea1d543795
commit 91e96aa4b9
No known key found for this signature in database
GPG Key ID: C16961E655E74B5E
22 changed files with 612 additions and 9 deletions

View File

@ -0,0 +1,51 @@
<?php
/**
* UserChangedEmail.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Events;
use FireflyIII\User;
use Illuminate\Queue\SerializesModels;
/**
* Class UserChangedEmail
*
* @package FireflyIII\Events
*/
class UserChangedEmail extends Event
{
use SerializesModels;
/** @var string */
public $ipAddress;
/** @var string */
public $newEmail;
/** @var string */
public $oldEmail;
/** @var User */
public $user;
/**
* UserChangedEmail constructor.
*
* @param User $user
* @param string $newEmail
* @param string $oldEmail
* @param string $ipAddress
*/
public function __construct(User $user, string $newEmail, string $oldEmail, string $ipAddress)
{
$this->user = $user;
$this->ipAddress = $ipAddress;
$this->oldEmail = $oldEmail;
$this->newEmail = $newEmail;
}
}

View File

@ -15,11 +15,15 @@ namespace FireflyIII\Handlers\Events;
use FireflyIII\Events\RegisteredUser; use FireflyIII\Events\RegisteredUser;
use FireflyIII\Events\RequestedNewPassword; use FireflyIII\Events\RequestedNewPassword;
use FireflyIII\Events\UserChangedEmail;
use FireflyIII\Mail\ConfirmEmailChangeMail;
use FireflyIII\Mail\RegisteredUser as RegisteredUserMail; use FireflyIII\Mail\RegisteredUser as RegisteredUserMail;
use FireflyIII\Mail\RequestedNewPassword as RequestedNewPasswordMail; use FireflyIII\Mail\RequestedNewPassword as RequestedNewPasswordMail;
use FireflyIII\Mail\UndoEmailChangeMail;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use Log; use Log;
use Mail; use Mail;
use Preferences;
use Swift_TransportException; use Swift_TransportException;
/** /**
@ -54,6 +58,54 @@ class UserEventHandler
return true; return true;
} }
/**
* @param UserChangedEmail $event
*
* @return bool
*/
public function sendEmailChangeConfirmMail(UserChangedEmail $event): bool
{
$newEmail = $event->newEmail;
$oldEmail = $event->oldEmail;
$user = $event->user;
$ipAddress = $event->ipAddress;
$token = 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));
// @codeCoverageIgnoreStart
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
// @codeCoverageIgnoreEnd
return true;
}
/**
* @param UserChangedEmail $event
*
* @return bool
*/
public function sendEmailChangeUndoMail(UserChangedEmail $event): bool
{
$newEmail = $event->newEmail;
$oldEmail = $event->oldEmail;
$user = $event->user;
$ipAddress = $event->ipAddress;
$token = Preferences::getForUser($user, 'email_change_undo_token', 'invalid');
$uri = route('profile.undo-email-change', [$token->data, hash('sha256', $oldEmail)]);
try {
Mail::to($oldEmail)->send(new UndoEmailChangeMail($newEmail, $oldEmail, $uri, $ipAddress));
// @codeCoverageIgnoreStart
} catch (Swift_TransportException $e) {
Log::error($e->getMessage());
}
// @codeCoverageIgnoreEnd
return true;
}
/** /**
* @param RequestedNewPassword $event * @param RequestedNewPassword $event
* *

View File

@ -14,6 +14,7 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers\Auth; namespace FireflyIII\Http\Controllers\Auth;
use FireflyConfig; use FireflyConfig;
use FireflyIII\Events\UserChangedEmail;
use FireflyIII\Http\Controllers\Controller; use FireflyIII\Http\Controllers\Controller;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Cookie\CookieJar; use Illuminate\Cookie\CookieJar;

View File

@ -13,10 +13,15 @@ declare(strict_types=1);
namespace FireflyIII\Http\Controllers; namespace FireflyIII\Http\Controllers;
use Auth;
use FireflyIII\Events\UserChangedEmail;
use FireflyIII\Exceptions\FireflyException;
use FireflyIII\Exceptions\ValidationException; use FireflyIII\Exceptions\ValidationException;
use FireflyIII\Http\Middleware\IsLimitedUser; use FireflyIII\Http\Middleware\IsLimitedUser;
use FireflyIII\Http\Requests\DeleteAccountFormRequest; use FireflyIII\Http\Requests\DeleteAccountFormRequest;
use FireflyIII\Http\Requests\EmailFormRequest;
use FireflyIII\Http\Requests\ProfileFormRequest; use FireflyIII\Http\Requests\ProfileFormRequest;
use FireflyIII\Models\Preference;
use FireflyIII\Repositories\User\UserRepositoryInterface; use FireflyIII\Repositories\User\UserRepositoryInterface;
use FireflyIII\User; use FireflyIII\User;
use Hash; use Hash;
@ -48,10 +53,23 @@ class ProfileController extends Controller
return $next($request); return $next($request);
} }
); );
$this->middleware(IsLimitedUser::class); $this->middleware(IsLimitedUser::class)->except(['confirmEmailChange', 'undoEmailChange']);
} }
/**
* @return View
*/
public function changeEmail()
{
$title = auth()->user()->email;
$email = auth()->user()->email;
$subTitle = strval(trans('firefly.change_your_email'));
$subTitleIcon = 'fa-envelope';
return view('profile.change-email', compact('title', 'subTitle', 'subTitleIcon', 'email'));
}
/** /**
* @return View * @return View
*/ */
@ -64,6 +82,37 @@ class ProfileController extends Controller
return view('profile.change-password', compact('title', 'subTitle', 'subTitleIcon')); return view('profile.change-password', compact('title', 'subTitle', 'subTitleIcon'));
} }
/**
* @param string $token
*
* @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
* @throws FireflyException
*/
public function confirmEmailChange(string $token)
{
// find preference with this token value.
$set = Preferences::findByName('email_change_confirm_token');
$user = null;
/** @var Preference $preference */
foreach ($set as $preference) {
if ($preference->data === $token) {
$user = $preference->user;
}
}
// update user to clear blocked and blocked_code.
if (is_null($user)) {
throw new FireflyException('Invalid token.');
}
$user->blocked = 0;
$user->blocked_code = '';
$user->save();
// return to login.
Session::flash('success', strval(trans('firefly.login_with_new_email')));
return redirect(route('login'));
}
/** /**
* @return View * @return View
*/ */
@ -95,6 +144,49 @@ class ProfileController extends Controller
return view('profile.index', compact('subTitle', 'userId', 'accessToken')); return view('profile.index', compact('subTitle', 'userId', 'accessToken'));
} }
/**
* @param EmailFormRequest $request
* @param UserRepositoryInterface $repository
*
* @return $this|\Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function postChangeEmail(EmailFormRequest $request, UserRepositoryInterface $repository)
{
/** @var User $user */
$user = auth()->user();
$newEmail = $request->string('email');
$oldEmail = $user->email;
if ($newEmail === $user->email) {
Session::flash('error', strval(trans('firefly.email_not_changed')));
return redirect(route('profile.change-email'))->withInput();
}
$existing = $repository->findByEmail($newEmail);
if (!is_null($existing)) {
// force user logout.
$this->guard()->logout();
$request->session()->invalidate();
Session::flash('success', strval(trans('firefly.email_changed')));
return redirect(route('index'));
}
// now actually update user:
$repository->changeEmail($user, $newEmail);
// call event.
$ipAddress = $request->ip();
event(new UserChangedEmail($user, $newEmail, $oldEmail, $ipAddress));
// force user logout.
Auth::guard()->logout();
$request->session()->invalidate();
Session::flash('success', strval(trans('firefly.email_changed')));
return redirect(route('index'));
}
/** /**
* @param ProfileFormRequest $request * @param ProfileFormRequest $request
* @param UserRepositoryInterface $repository * @param UserRepositoryInterface $repository
@ -160,6 +252,53 @@ class ProfileController extends Controller
return redirect(route('profile.index')); return redirect(route('profile.index'));
} }
/**
* @param string $token
* @param string $hash
*
* @throws FireflyException
*/
public function undoEmailChange(string $token, string $hash)
{
// find preference with this token value.
$set = Preferences::findByName('email_change_undo_token');
$user = null;
/** @var Preference $preference */
foreach ($set as $preference) {
if ($preference->data === $token) {
$user = $preference->user;
}
}
if (is_null($user)) {
throw new FireflyException('Invalid token.');
}
// found user.
// which email address to return to?
$set = Preferences::beginsWith($user, 'previous_email_');
$match = null;
foreach ($set as $entry) {
$hashed = hash('sha256', $entry->data);
if ($hashed === $hash) {
$match = $entry->data;
break;
}
}
if (is_null($match)) {
throw new FireflyException('Invalid token.');
}
// change user back
$user->email = $match;
$user->blocked = 0;
$user->blocked_code = '';
$user->save();
// return to login.
Session::flash('success', strval(trans('firefly.login_with_old_email')));
return redirect(route('login'));
}
/** /**
* @param User $user * @param User $user
* @param string $current * @param string $current

View File

@ -44,8 +44,13 @@ class Authenticate
return redirect()->guest('login'); return redirect()->guest('login');
} }
if (intval(auth()->user()->blocked) === 1) { if (intval(auth()->user()->blocked) === 1) {
$message = strval(trans('firefly.block_account_logout'));
if (auth()->user()->blocked_code === 'email_changed') {
$message = strval(trans('firefly.email_changed_logout'));
}
Session::flash('logoutMessage', $message);
Auth::guard($guard)->logout(); Auth::guard($guard)->logout();
Session::flash('logoutMessage', trans('firefly.block_account_logout'));
return redirect()->guest('login'); return redirect()->guest('login');
} }

View File

@ -0,0 +1,42 @@
<?php
/**
* EmailFormRequest.php
* Copyright (c) 2017 thegrumpydictator@gmail.com
* This software may be modified and distributed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International License.
*
* See the LICENSE file for details.
*/
declare(strict_types=1);
namespace FireflyIII\Http\Requests;
/**
* Class EmailFormRequest
*
*
* @package FireflyIII\Http\Requests
*/
class EmailFormRequest extends Request
{
/**
* @return bool
*/
public function authorize()
{
// Only allow logged in users
return auth()->check();
}
/**
* @return array
*/
public function rules()
{
// fixed
return [
'email' => 'required|email',
];
}
}

View File

@ -0,0 +1,49 @@
<?php
namespace FireflyIII\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class ConfirmEmailChangeMail extends Mailable
{
use Queueable, SerializesModels;
/** @var string */
public $ipAddress;
/** @var string */
public $newEmail;
/** @var string */
public $oldEmail;
/** @var string */
public $uri;
/**
* ConfirmEmailChangeMail constructor.
*
* @param string $newEmail
* @param string $oldEmail
* @param string $uri
* @param string $ipAddress
*/
public function __construct(string $newEmail, string $oldEmail, string $uri, string $ipAddress)
{
$this->newEmail = $newEmail;
$this->oldEmail = $oldEmail;
$this->uri = $uri;
$this->ipAddress = $ipAddress;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('emails.confirm-email-change-html')->text('emails.confirm-email-change-text')
->subject('Your Firefly III email address has changed.');
}
}

View File

@ -0,0 +1,50 @@
<?php
namespace FireflyIII\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Illuminate\Contracts\Queue\ShouldQueue;
class UndoEmailChangeMail extends Mailable
{
use Queueable, SerializesModels;
/** @var string */
public $ipAddress;
/** @var string */
public $newEmail;
/** @var string */
public $oldEmail;
/** @var string */
public $uri;
/**
* UndoEmailChangeMail constructor.
*
* @param string $newEmail
* @param string $oldEmail
* @param string $uri
* @param string $ipAddress
*/
public function __construct(string $newEmail, string $oldEmail, string $uri, string $ipAddress)
{
$this->newEmail = $newEmail;
$this->oldEmail = $oldEmail;
$this->uri = $uri;
$this->ipAddress = $ipAddress;
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('emails.undo-email-change-html')->text('emails.undo-email-change-text')
->subject('Your Firefly III email address has changed.');
}
}

View File

@ -36,26 +36,35 @@ class EventServiceProvider extends ServiceProvider
*/ */
protected $listen protected $listen
= [ = [
// new event handlers: // is a User related event.
'FireflyIII\Events\RegisteredUser' => // is a User related event. 'FireflyIII\Events\RegisteredUser' =>
[ [
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationMail', 'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationMail',
'FireflyIII\Handlers\Events\UserEventHandler@attachUserRole', 'FireflyIII\Handlers\Events\UserEventHandler@attachUserRole',
], ],
'FireflyIII\Events\RequestedNewPassword' => [ // is a User related event. // is a User related event.
'FireflyIII\Handlers\Events\UserEventHandler@sendNewPassword', 'FireflyIII\Events\RequestedNewPassword' => [
'FireflyIII\Handlers\Events\UserEventHandler@sendNewPassword',
], ],
'FireflyIII\Events\StoredTransactionJournal' => // is a Transaction Journal related event. // is a User related event.
'FireflyIII\Events\UserChangedEmail' => [
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeConfirmMail',
'FireflyIII\Handlers\Events\UserEventHandler@sendEmailChangeUndoMail',
],
// is a Transaction Journal related event.
'FireflyIII\Events\StoredTransactionJournal' =>
[ [
'FireflyIII\Handlers\Events\StoredJournalEventHandler@scanBills', 'FireflyIII\Handlers\Events\StoredJournalEventHandler@scanBills',
'FireflyIII\Handlers\Events\StoredJournalEventHandler@connectToPiggyBank', 'FireflyIII\Handlers\Events\StoredJournalEventHandler@connectToPiggyBank',
'FireflyIII\Handlers\Events\StoredJournalEventHandler@processRules', 'FireflyIII\Handlers\Events\StoredJournalEventHandler@processRules',
], ],
'FireflyIII\Events\UpdatedTransactionJournal' => // is a Transaction Journal related event. // is a Transaction Journal related event.
'FireflyIII\Events\UpdatedTransactionJournal' =>
[ [
'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@scanBills', 'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@scanBills',
'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@processRules', 'FireflyIII\Handlers\Events\UpdatedJournalEventHandler@processRules',
], ],
]; ];
/** /**

View File

@ -52,6 +52,33 @@ class UserRepository implements UserRepositoryInterface
return true; return true;
} }
/**
* @param User $user
* @param string $newEmail
*
* @return bool
*/
public function changeEmail(User $user, string $newEmail): bool
{
$oldEmail = $user->email;
// save old email as pref
Preferences::setForUser($user, 'previous_email_latest', $oldEmail);
Preferences::setForUser($user, 'previous_email_' . date('Y-m-d-H-i-s'), $oldEmail);
// set undo and confirm token:
Preferences::setForUser($user, 'email_change_undo_token', strval(bin2hex(random_bytes(16))));
Preferences::setForUser($user, 'email_change_confirm_token', strval(bin2hex(random_bytes(16))));
// update user
$user->email = $newEmail;
$user->blocked = 1;
$user->blocked_code = 'email_changed';
$user->save();
return true;
}
/** /**
* @param User $user * @param User $user
* @param string $password * @param string $password
@ -119,6 +146,16 @@ class UserRepository implements UserRepositoryInterface
return new User; return new User;
} }
/**
* @param string $email
*
* @return User|null
*/
public function findByEmail(string $email): ?User
{
return User::where('email', $email)->first();
}
/** /**
* Return basic user information. * Return basic user information.
* *

View File

@ -42,6 +42,14 @@ interface UserRepositoryInterface
*/ */
public function attachRole(User $user, string $role): bool; public function attachRole(User $user, string $role): bool;
/**
* @param User $user
* @param string $newEmail
*
* @return bool
*/
public function changeEmail(User $user, string $newEmail): bool;
/** /**
* @param User $user * @param User $user
* @param string $password * @param string $password
@ -80,6 +88,13 @@ interface UserRepositoryInterface
*/ */
public function find(int $userId): User; public function find(int $userId): User;
/**
* @param string $email
*
* @return User|null
*/
public function findByEmail(string $email): ?User;
/** /**
* Return basic user information. * Return basic user information.
* *

View File

@ -16,6 +16,7 @@ namespace FireflyIII\Support;
use Cache; use Cache;
use FireflyIII\Models\Preference; use FireflyIII\Models\Preference;
use FireflyIII\User; use FireflyIII\User;
use Illuminate\Support\Collection;
use Session; use Session;
/** /**
@ -25,13 +26,26 @@ use Session;
*/ */
class Preferences class Preferences
{ {
/**
* @param User $user
* @param string $search
*
* @return Collection
*/
public function beginsWith(User $user, string $search): Collection
{
$set = Preference::where('user_id', $user->id)->where('name', 'LIKE', $search . '%')->get();
return $set;
}
/** /**
* @param $name * @param $name
* *
* @return bool * @return bool
* @throws \Exception * @throws \Exception
*/ */
public function delete($name): bool public function delete(string $name): bool
{ {
$fullName = sprintf('preference%s%s', auth()->user()->id, $name); $fullName = sprintf('preference%s%s', auth()->user()->id, $name);
if (Cache::has($fullName)) { if (Cache::has($fullName)) {
@ -42,6 +56,18 @@ class Preferences
return true; return true;
} }
/**
* @param string $name
*
* @return Collection
*/
public function findByName(string $name): Collection
{
$set = Preference::where('name', $name)->get();
return $set;
}
/** /**
* @param $name * @param $name
* @param null $default * @param null $default

View File

@ -424,6 +424,11 @@ return [
'explain_access_token' => 'You need this token to perform command line options, such as importing or exporting data. Without it, such sensitive commands will not work. Do not share your access token. Nobody will ask you for this token, not even me. If you fear you lost this, or when you\'re paranoid, regenerate this token using the button.', 'explain_access_token' => 'You need this token to perform command line options, such as importing or exporting data. Without it, such sensitive commands will not work. Do not share your access token. Nobody will ask you for this token, not even me. If you fear you lost this, or when you\'re paranoid, regenerate this token using the button.',
'regenerate_access_token' => 'Regenerate access token', 'regenerate_access_token' => 'Regenerate access token',
'token_regenerated' => 'A new token was generated', 'token_regenerated' => 'A new token was generated',
'change_your_email' => 'Change your email address',
'email_verification' => 'An email message will be sent to your old AND new email address. For security purposes, you will not be able to login until you verify your new email address. If you are unsure if your Firefly III installation is capable of sending email, please do not use this feature. You can test this in the <a href="/admin">Administration</a>.',
'email_changed_logout' => 'Until you verify your email address, you cannot login.',
'login_with_new_email' => 'You can now login with your new email address.',
'login_with_old_email' => 'You can now login with your old email address again.',
// attachments // attachments
'nr_of_attachments' => 'One attachment|:count attachments', 'nr_of_attachments' => 'One attachment|:count attachments',

View File

@ -67,6 +67,8 @@ return [
'source_amount' => 'Amount (source)', 'source_amount' => 'Amount (source)',
'destination_amount' => 'Amount (destination)', 'destination_amount' => 'Amount (destination)',
'native_amount' => 'Native amount', 'native_amount' => 'Native amount',
'new_email_address' => 'New email address',
'verification' => 'Verification',
'source_account_asset' => 'Source account (asset account)', 'source_account_asset' => 'Source account (asset account)',
'destination_account_expense' => 'Destination account (expense account)', 'destination_account_expense' => 'Destination account (expense account)',

View File

@ -13,6 +13,16 @@
</div> </div>
{% endif %} {% endif %}
{# SUCCESS MESSAGE (ALWAYS SINGULAR) #}
{% if Session.has('success') %}
<div class="alert alert-success alert-dismissible" role="alert">
<button type="button" class="close" data-dismiss="alert">
<span>&times;</span><span class="sr-only">{{ 'close'|_ }}</span>
</button>
<strong>{{ 'flash_success'|_ }}</strong> {{ session('success') }}
</div>
{% endif %}
{% if errors.has('email') %} {% if errors.has('email') %}
<div class="row"> <div class="row">
<div class="col-lg-12"> <div class="col-lg-12">

View File

@ -0,0 +1,18 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
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.
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
The old email addres was: {{ oldEmail }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
The new email address is: <strong>{{ newEmail }}</strong>
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
You cannot use Firefly III until you confirm this change. Please follow the link below to do so.
</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

@ -0,0 +1,9 @@
{% include 'emails.header-text' %}
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.
The old email addres was: {{ oldEmail }}
The new email address is: {{ newEmail }}
You cannot use Firefly III until you confirm this change. Please follow the link to do so: {{ uri }}
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,18 @@
{% include 'emails.header-html' %}
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
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!
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
If you initiated this change, you may safely ignore this message.
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
The old email addres was: <strong>{{ oldEmail }}</strong>
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
The new email address is: {{ newEmail }}
</p>
<p style="font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;font-size:13px;">
To undo the change, follow this link: <a href="{{ uri }}">{{ uri }}</a>
</p>
{% include 'emails.footer-html' %}

View File

@ -0,0 +1,12 @@
{% include 'emails.header-text' %}
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!
If you initiated this change, you may safely ignore this message.
The old email addres was: {{ oldEmail }}
The new email address is: {{ newEmail }}
To undo the change, follow this link: {{ uri }}
{% include 'emails.footer-text' %}

View File

@ -0,0 +1,47 @@
{% extends "./layout/default" %}
{% block breadcrumbs %}
{{ Breadcrumbs.renderIfExists(Route.getCurrentRoute.getName) }}
{% endblock %}
{% block content %}
<form method="POST" action="{{ route('profile.change-email.post') }}" accept-charset="UTF-8" class="form-horizontal" id="change-password">
<input name="_token" type="hidden" value="{{ csrf_token() }}">
<div class="row">
<div class="col-lg-6 col-md-12 col-sm-12">
<div class="box box-primary">
<div class="box-header with-border">
<h3 class="box-title">{{ 'change_your_email'|_ }}</h3>
</div>
<div class="box-body">
{% if errors|length > 0 %}
<ul>
{% for error in errors.all %}
<li class="text-danger">{{ error }}</li>
{% endfor %}
</ul>
{% endif %}
<div class="form-group">
<label for="email" class="col-sm-4 control-label">{{ trans('form.new_email_address') }}</label>
<div class="col-sm-8">
<input type="email" class="form-control" id="email" placeholder="{{ 'new_email_address'|_ }}"
value="{{ old('email')|default(email) }}"
name="email">
</div>
</div>
{{ ExpandedForm.staticText('verification',trans('firefly.email_verification')) }}
</div>
<div class="box-footer">
<button type="submit" class="btn btn-success pull-right">{{ 'change_your_email'|_ }}</button>
</div>
</div>
</div>
</div>
</form>
{% endblock %}

View File

@ -16,6 +16,7 @@
{{ trans('firefly.user_id_is',{user: userId})|raw }} {{ trans('firefly.user_id_is',{user: userId})|raw }}
</p> </p>
<ul> <ul>
<li><a href="{{ route('profile.change-email') }}">{{ 'change_your_email'|_ }}</a></li>
<li><a href="{{ route('profile.change-password') }}">{{ 'change_your_password'|_ }}</a></li> <li><a href="{{ route('profile.change-password') }}">{{ 'change_your_password'|_ }}</a></li>
<li><a class="text-danger" href="{{ route('profile.delete-account') }}">{{ 'delete_account'|_ }}</a></li> <li><a class="text-danger" href="{{ route('profile.delete-account') }}">{{ 'delete_account'|_ }}</a></li>
</ul> </ul>

View File

@ -32,6 +32,9 @@ Route::group(
Route::post('password/reset', 'Auth\ResetPasswordController@reset'); Route::post('password/reset', 'Auth\ResetPasswordController@reset');
Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm'); Route::get('password/reset', 'Auth\ForgotPasswordController@showLinkRequestForm');
// Change email routes:
Route::get('profile/confirm-email-change/{token}', ['uses' => 'ProfileController@confirmEmailChange', 'as' => 'profile.confirm-email-change']);
Route::get('profile/undo-email-change/{token}/{oldAddressHash}', ['uses' => 'ProfileController@undoEmailChange', 'as' => 'profile.undo-email-change']);
} }
); );
@ -521,11 +524,13 @@ Route::group(
['middleware' => 'user-full-auth', 'prefix' => 'profile', 'as' => 'profile.'], function () { ['middleware' => 'user-full-auth', 'prefix' => 'profile', 'as' => 'profile.'], function () {
Route::get('', ['uses' => 'ProfileController@index', 'as' => 'index']); Route::get('', ['uses' => 'ProfileController@index', 'as' => 'index']);
Route::get('change-email', ['uses' => 'ProfileController@changeEmail', 'as' => 'change-email']);
Route::get('change-password', ['uses' => 'ProfileController@changePassword', 'as' => 'change-password']); Route::get('change-password', ['uses' => 'ProfileController@changePassword', 'as' => 'change-password']);
Route::get('delete-account', ['uses' => 'ProfileController@deleteAccount', 'as' => 'delete-account']); Route::get('delete-account', ['uses' => 'ProfileController@deleteAccount', 'as' => 'delete-account']);
Route::post('delete-account', ['uses' => 'ProfileController@postDeleteAccount', 'as' => 'delete-account.post']); Route::post('delete-account', ['uses' => 'ProfileController@postDeleteAccount', 'as' => 'delete-account.post']);
Route::post('change-password', ['uses' => 'ProfileController@postChangePassword', 'as' => 'change-password.post']); Route::post('change-password', ['uses' => 'ProfileController@postChangePassword', 'as' => 'change-password.post']);
Route::post('change-email', ['uses' => 'ProfileController@postChangeEmail', 'as' => 'change-email.post']);
Route::post('regenerate', ['uses' => 'ProfileController@regenerate', 'as' => 'regenerate']); Route::post('regenerate', ['uses' => 'ProfileController@regenerate', 'as' => 'regenerate']);
} }
); );