mirror of
https://github.com/firefly-iii/firefly-iii.git
synced 2025-12-12 01:42:32 +00:00
New code for email address change in profile. See #857
This commit is contained in:
51
app/Events/UserChangedEmail.php
Normal file
51
app/Events/UserChangedEmail.php
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -15,11 +15,15 @@ namespace FireflyIII\Handlers\Events;
|
||||
|
||||
use FireflyIII\Events\RegisteredUser;
|
||||
use FireflyIII\Events\RequestedNewPassword;
|
||||
use FireflyIII\Events\UserChangedEmail;
|
||||
use FireflyIII\Mail\ConfirmEmailChangeMail;
|
||||
use FireflyIII\Mail\RegisteredUser as RegisteredUserMail;
|
||||
use FireflyIII\Mail\RequestedNewPassword as RequestedNewPasswordMail;
|
||||
use FireflyIII\Mail\UndoEmailChangeMail;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use Log;
|
||||
use Mail;
|
||||
use Preferences;
|
||||
use Swift_TransportException;
|
||||
|
||||
/**
|
||||
@@ -54,6 +58,54 @@ class UserEventHandler
|
||||
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
|
||||
*
|
||||
|
||||
@@ -14,6 +14,7 @@ declare(strict_types=1);
|
||||
namespace FireflyIII\Http\Controllers\Auth;
|
||||
|
||||
use FireflyConfig;
|
||||
use FireflyIII\Events\UserChangedEmail;
|
||||
use FireflyIII\Http\Controllers\Controller;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Cookie\CookieJar;
|
||||
|
||||
@@ -13,10 +13,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace FireflyIII\Http\Controllers;
|
||||
|
||||
use Auth;
|
||||
use FireflyIII\Events\UserChangedEmail;
|
||||
use FireflyIII\Exceptions\FireflyException;
|
||||
use FireflyIII\Exceptions\ValidationException;
|
||||
use FireflyIII\Http\Middleware\IsLimitedUser;
|
||||
use FireflyIII\Http\Requests\DeleteAccountFormRequest;
|
||||
use FireflyIII\Http\Requests\EmailFormRequest;
|
||||
use FireflyIII\Http\Requests\ProfileFormRequest;
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\Repositories\User\UserRepositoryInterface;
|
||||
use FireflyIII\User;
|
||||
use Hash;
|
||||
@@ -48,10 +53,23 @@ class ProfileController extends Controller
|
||||
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
|
||||
*/
|
||||
@@ -64,6 +82,37 @@ class ProfileController extends Controller
|
||||
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
|
||||
*/
|
||||
@@ -95,6 +144,49 @@ class ProfileController extends Controller
|
||||
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 UserRepositoryInterface $repository
|
||||
@@ -160,6 +252,53 @@ class ProfileController extends Controller
|
||||
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 string $current
|
||||
|
||||
@@ -44,8 +44,13 @@ class Authenticate
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
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();
|
||||
Session::flash('logoutMessage', trans('firefly.block_account_logout'));
|
||||
|
||||
return redirect()->guest('login');
|
||||
}
|
||||
|
||||
42
app/Http/Requests/EmailFormRequest.php
Normal file
42
app/Http/Requests/EmailFormRequest.php
Normal 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',
|
||||
];
|
||||
}
|
||||
}
|
||||
49
app/Mail/ConfirmEmailChangeMail.php
Normal file
49
app/Mail/ConfirmEmailChangeMail.php
Normal 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.');
|
||||
}
|
||||
}
|
||||
50
app/Mail/UndoEmailChangeMail.php
Normal file
50
app/Mail/UndoEmailChangeMail.php
Normal 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.');
|
||||
}
|
||||
}
|
||||
@@ -36,26 +36,35 @@ class EventServiceProvider extends ServiceProvider
|
||||
*/
|
||||
protected $listen
|
||||
= [
|
||||
// new event handlers:
|
||||
'FireflyIII\Events\RegisteredUser' => // is a User related event.
|
||||
// is a User related event.
|
||||
'FireflyIII\Events\RegisteredUser' =>
|
||||
[
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@sendRegistrationMail',
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@attachUserRole',
|
||||
],
|
||||
'FireflyIII\Events\RequestedNewPassword' => [ // is a User related event.
|
||||
'FireflyIII\Handlers\Events\UserEventHandler@sendNewPassword',
|
||||
// is a User related event.
|
||||
'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@connectToPiggyBank',
|
||||
'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@processRules',
|
||||
],
|
||||
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -52,6 +52,33 @@ class UserRepository implements UserRepositoryInterface
|
||||
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 string $password
|
||||
@@ -119,6 +146,16 @@ class UserRepository implements UserRepositoryInterface
|
||||
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.
|
||||
*
|
||||
|
||||
@@ -42,6 +42,14 @@ interface UserRepositoryInterface
|
||||
*/
|
||||
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 string $password
|
||||
@@ -80,6 +88,13 @@ interface UserRepositoryInterface
|
||||
*/
|
||||
public function find(int $userId): User;
|
||||
|
||||
/**
|
||||
* @param string $email
|
||||
*
|
||||
* @return User|null
|
||||
*/
|
||||
public function findByEmail(string $email): ?User;
|
||||
|
||||
/**
|
||||
* Return basic user information.
|
||||
*
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace FireflyIII\Support;
|
||||
use Cache;
|
||||
use FireflyIII\Models\Preference;
|
||||
use FireflyIII\User;
|
||||
use Illuminate\Support\Collection;
|
||||
use Session;
|
||||
|
||||
/**
|
||||
@@ -25,13 +26,26 @@ use Session;
|
||||
*/
|
||||
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
|
||||
*
|
||||
* @return bool
|
||||
* @throws \Exception
|
||||
*/
|
||||
public function delete($name): bool
|
||||
public function delete(string $name): bool
|
||||
{
|
||||
$fullName = sprintf('preference%s%s', auth()->user()->id, $name);
|
||||
if (Cache::has($fullName)) {
|
||||
@@ -42,6 +56,18 @@ class Preferences
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public function findByName(string $name): Collection
|
||||
{
|
||||
$set = Preference::where('name', $name)->get();
|
||||
|
||||
return $set;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @param null $default
|
||||
|
||||
Reference in New Issue
Block a user