mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-05-24 02:14:21 -04:00
Finished - Client auth + password reset + mailables
This commit is contained in:
parent
7e4294fcc5
commit
f63803fe7b
@ -13,6 +13,8 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\SendsPasswordResetEmails;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
|
||||
class ContactForgotPasswordController extends Controller
|
||||
{
|
||||
@ -36,11 +38,28 @@ class ContactForgotPasswordController extends Controller
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
$this->middleware('guest:contact');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the reset email form.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function showLinkRequestForm(){
|
||||
return view('portal.default.auth.passwords.email',[
|
||||
'title' => 'Client Password Reset',
|
||||
'passwordEmailRoute' => 'client.password.email'
|
||||
]);
|
||||
}
|
||||
|
||||
protected function guard()
|
||||
{
|
||||
return Auth::guard('contact');
|
||||
}
|
||||
|
||||
public function broker()
|
||||
{
|
||||
return Password::broker('contacts');
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,9 @@ namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
|
||||
class ContactResetPasswordController extends Controller
|
||||
{
|
||||
@ -34,7 +37,7 @@ class ContactResetPasswordController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/client';
|
||||
protected $redirectTo = '/client/dashboard';
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
@ -43,11 +46,32 @@ class ContactResetPasswordController extends Controller
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
$this->middleware('guest:contact');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the password reset view for the given token.
|
||||
*
|
||||
* If no token is present, display the link request form.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param string|null $token
|
||||
* @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
|
||||
*/
|
||||
public function showResetForm(Request $request, $token = null)
|
||||
{
|
||||
return view('portal.default.auth.passwords.reset')->with(
|
||||
['token' => $token, 'email' => $request->email]
|
||||
);
|
||||
}
|
||||
|
||||
protected function guard()
|
||||
{
|
||||
return Auth::guard('contact');
|
||||
}
|
||||
|
||||
protected function broker()
|
||||
{
|
||||
return Password::broker('contacts');
|
||||
}
|
||||
}
|
||||
|
@ -13,6 +13,8 @@ namespace App\Models;
|
||||
|
||||
use App\Models\Company;
|
||||
use App\Models\User;
|
||||
use App\Notifications\ClientContactResetPassword as ResetPasswordNotification;
|
||||
use App\Notifications\ClientContactResetPassword;
|
||||
use App\Utils\Traits\MakesHash;
|
||||
use Hashids\Hashids;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
@ -89,4 +91,8 @@ class ClientContact extends Authenticatable
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
public function sendPasswordResetNotification($token)
|
||||
{
|
||||
$this->notify(new ClientContactResetPassword($token));
|
||||
}
|
||||
}
|
||||
|
@ -14,6 +14,7 @@
|
||||
"@coreui/coreui": "^2.1.12",
|
||||
"@coreui/icons": "^0.3.0",
|
||||
"bootstrap": "^4.3.1",
|
||||
"cross-env": "^5.2.0",
|
||||
"font-awesome": "^4.7.0",
|
||||
"laravel-mix": "^4.1.2",
|
||||
"puppeteer": "^1.18.1"
|
||||
|
@ -30,27 +30,6 @@
|
||||
<meta name="description" content="@yield('meta_description')"/>
|
||||
<link href="{{ asset('favicon.png') }}" rel="shortcut icon" type="image/png">
|
||||
|
||||
<!--
|
||||
TODO Setup social sharing info
|
||||
<meta property="og:site_name" content="Invoice Ninja"/>
|
||||
<meta property="og:url" content="{{ config('ninja.site_url') }}"/>
|
||||
<meta property="og:title" content="Invoice Ninja"/>
|
||||
<meta property="og:image" content="{{ config('ninja.site_url') }}/images/logo.png"/>
|
||||
<meta property="og:description" content="Create. Send. Get Paid."/>
|
||||
--/>
|
||||
<!-- http://realfavicongenerator.net -->
|
||||
<!--
|
||||
TODO Setup favicon
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{{ url('apple-touch-icon.png') }}">
|
||||
<link rel="icon" type="image/png" href="{{ url('favicon-32x32.png') }}" sizes="32x32">
|
||||
<link rel="icon" type="image/png" href="{{ url('favicon-16x16.png') }}" sizes="16x16">
|
||||
<link rel="manifest" href="{{ url('manifest.json') }}">
|
||||
<link rel="mask-icon" href="{{ url('safari-pinned-tab.svg') }}" color="#3bc65c">
|
||||
<link rel="shortcut icon" href="{{ url('favicon.ico') }}">
|
||||
<meta name="apple-mobile-web-app-title" content="Invoice Ninja">
|
||||
<meta name="application-name" content="Invoice Ninja">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
-->
|
||||
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
@ -58,12 +37,10 @@
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/simple-line-icons/2.4.1/css/simple-line-icons.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.css">
|
||||
<link rel="canonical" href="{{ config('ninja.site_url') }}/{{ request()->path() }}"/>
|
||||
<link rel="stylesheet" href="{{ mix('/css/ninja.min.css') }}">
|
||||
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
|
||||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
|
||||
<script src=" {{ mix('/js/coreui.min.js') }}"></script>
|
||||
<script defer src=" {{ mix('/js/ninja.min.js') }}"></script>
|
||||
|
||||
|
||||
@yield('head')
|
||||
|
||||
|
@ -0,0 +1,51 @@
|
||||
@extends('portal.default.layouts.guest')
|
||||
|
||||
@section('body')
|
||||
<body class="app flex-row align-items-center">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card-group">
|
||||
<div class="card p-4">
|
||||
<div class="card-body">
|
||||
@if (session('status'))
|
||||
<div class="alert alert-success" role="alert">
|
||||
{{ session('status') }}
|
||||
</div>
|
||||
@endif
|
||||
<form method="POST" action="{{ route($passwordEmailRoute) }}">
|
||||
@csrf
|
||||
<h1>@lang('texts.password_recovery')</h1>
|
||||
<p class="text-muted"></p>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="icon-user"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" placeholder="@lang('texts.email')" required autofocus>
|
||||
|
||||
@if ($errors->has('email'))
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $errors->first('email') }}</strong>
|
||||
</span>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<button class="btn btn-primary px-4" type="submit">@lang('texts.send_email')</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
@endsection
|
||||
|
||||
|
@ -0,0 +1,71 @@
|
||||
@extends('portal.default.layouts.guest')
|
||||
|
||||
@section('body')
|
||||
<body class="app flex-row align-items-center">
|
||||
<div class="container">
|
||||
<div class="row justify-content-center">
|
||||
<div class="col-md-8">
|
||||
<div class="card-group">
|
||||
<div class="card p-4">
|
||||
<div class="card-body">
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('client.password.update') }}">
|
||||
@csrf
|
||||
<input type="hidden" name="token" value="{{ $token }}">
|
||||
<h1>@lang('texts.change_password')</h1>
|
||||
<p class="text-muted"></p>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="icon-user"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input id="email" type="email" class="form-control{{ $errors->has('email') ? ' is-invalid' : '' }}" name="email" value="{{ old('email') }}" placeholder="@lang('texts.email')" required autofocus>
|
||||
|
||||
@if ($errors->has('email'))
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $errors->first('email') }}</strong>
|
||||
</span>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-4">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="icon-lock"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input id="password" type="password" class="form-control{{ $errors->has('password') ? ' is-invalid' : '' }}" name="password" placeholder="@lang('texts.password')" required>
|
||||
|
||||
@if ($errors->has('password'))
|
||||
<span class="invalid-feedback" role="alert">
|
||||
<strong>{{ $errors->first('password') }}</strong>
|
||||
</span>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-4">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">
|
||||
<i class="icon-lock"></i>
|
||||
</span>
|
||||
</div>
|
||||
<input id="password-confirm" type="password" class="form-control" name="password_confirmation" placeholder="@lang('texts.confirm_password')" required>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<button class="btn btn-primary px-4" type="submit">@lang('texts.change_password')</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
@endsection
|
1
resources/views/vendor/mail/text/button.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/button.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{ $slot }}: {{ $url }}
|
1
resources/views/vendor/mail/text/footer.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/footer.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{ $slot }}
|
1
resources/views/vendor/mail/text/header.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/header.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
||||
[{{ $slot }}]({{ $url }})
|
11
resources/views/vendor/mail/text/layout.blade.php
vendored
Normal file
11
resources/views/vendor/mail/text/layout.blade.php
vendored
Normal file
@ -0,0 +1,11 @@
|
||||
@if(!is_array($header))
|
||||
{!! strip_tags($header) !!}
|
||||
@endisset
|
||||
|
||||
{!! strip_tags($slot) !!}
|
||||
@isset($subcopy)
|
||||
|
||||
{!! strip_tags($subcopy) !!}
|
||||
@endisset
|
||||
|
||||
{!! strip_tags($footer) !!}
|
27
resources/views/vendor/mail/text/message.blade.php
vendored
Normal file
27
resources/views/vendor/mail/text/message.blade.php
vendored
Normal 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
|
1
resources/views/vendor/mail/text/panel.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/panel.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{ $slot }}
|
1
resources/views/vendor/mail/text/promotion.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/promotion.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{ $slot }}
|
1
resources/views/vendor/mail/text/promotion/button.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/promotion/button.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
||||
[{{ $slot }}]({{ $url }})
|
1
resources/views/vendor/mail/text/subcopy.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/subcopy.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{ $slot }}
|
1
resources/views/vendor/mail/text/table.blade.php
vendored
Normal file
1
resources/views/vendor/mail/text/table.blade.php
vendored
Normal file
@ -0,0 +1 @@
|
||||
{{ $slot }}
|
62
resources/views/vendor/notifications/email.blade.php
vendored
Normal file
62
resources/views/vendor/notifications/email.blade.php
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
@component('mail::message')
|
||||
{{-- Greeting --}}
|
||||
@if (! empty($greeting))
|
||||
# {{ $greeting }}
|
||||
@else
|
||||
@if ($level === 'error')
|
||||
# @lang('Whoops!')
|
||||
@else
|
||||
# @lang('Hello!')
|
||||
@endif
|
||||
@endif
|
||||
|
||||
{{-- Intro Lines --}}
|
||||
@foreach ($introLines as $line)
|
||||
{{ $line }}
|
||||
|
||||
@endforeach
|
||||
|
||||
{{-- Action Button --}}
|
||||
@isset($actionText)
|
||||
<?php
|
||||
switch ($level) {
|
||||
case 'success':
|
||||
case 'error':
|
||||
$color = $level;
|
||||
break;
|
||||
default:
|
||||
$color = 'primary';
|
||||
}
|
||||
?>
|
||||
@component('mail::button', ['url' => $actionUrl, 'color' => $color])
|
||||
{{ $actionText }}
|
||||
@endcomponent
|
||||
@endisset
|
||||
|
||||
{{-- Outro Lines --}}
|
||||
@foreach ($outroLines as $line)
|
||||
{{ $line }}
|
||||
|
||||
@endforeach
|
||||
|
||||
{{-- Salutation --}}
|
||||
@if (! empty($salutation))
|
||||
{{ $salutation }}
|
||||
@else
|
||||
@lang('Regards'),<br>{{ config('app.name') }}
|
||||
@endif
|
||||
|
||||
{{-- Subcopy --}}
|
||||
@isset($actionText)
|
||||
@slot('subcopy')
|
||||
@lang(
|
||||
"If you’re having trouble clicking the \":actionText\" button, copy and paste the URL below\n".
|
||||
'into your web browser: [:actionURL](:actionURL)',
|
||||
[
|
||||
'actionText' => $actionText,
|
||||
'actionURL' => $actionUrl,
|
||||
]
|
||||
)
|
||||
@endslot
|
||||
@endisset
|
||||
@endcomponent
|
Loading…
x
Reference in New Issue
Block a user