mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-08 20:22:42 +01:00
Merge branch 'develop' of github.com:hillelcoren/invoice-ninja into develop
This commit is contained in:
commit
1fb74a0355
@ -151,7 +151,7 @@ class AccountController extends BaseController
|
||||
} elseif ($section == ACCOUNT_INVOICE_DESIGN || $section == ACCOUNT_CUSTOMIZE_DESIGN) {
|
||||
return self::showInvoiceDesign($section);
|
||||
} elseif ($section == ACCOUNT_CLIENT_PORTAL) {
|
||||
return self::showClientViewStyling();
|
||||
return self::showClientPortal();
|
||||
} elseif ($section === ACCOUNT_TEMPLATES_AND_REMINDERS) {
|
||||
return self::showTemplates();
|
||||
} elseif ($section === ACCOUNT_PRODUCTS) {
|
||||
@ -414,7 +414,7 @@ class AccountController extends BaseController
|
||||
return View::make("accounts.{$section}", $data);
|
||||
}
|
||||
|
||||
private function showClientViewStyling()
|
||||
private function showClientPortal()
|
||||
{
|
||||
$account = Auth::user()->account->load('country');
|
||||
$css = $account->client_view_css ? $account->client_view_css : '';
|
||||
@ -430,6 +430,8 @@ class AccountController extends BaseController
|
||||
|
||||
$data = [
|
||||
'client_view_css' => $css,
|
||||
'enable_portal_password' => $account->enable_portal_password,
|
||||
'send_portal_password' => $account->send_portal_password,
|
||||
'title' => trans("texts.client_portal"),
|
||||
'section' => ACCOUNT_CLIENT_PORTAL,
|
||||
'account' => $account,
|
||||
@ -545,7 +547,11 @@ class AccountController extends BaseController
|
||||
|
||||
$account = Auth::user()->account;
|
||||
$account->client_view_css = $sanitized_css;
|
||||
$account->enable_client_portal = Input::get('enable_client_portal') ? true : false;
|
||||
|
||||
$account->enable_client_portal = !!Input::get('enable_client_portal');
|
||||
$account->enable_portal_password = !!Input::get('enable_portal_password');
|
||||
$account->send_portal_password = !!Input::get('send_portal_password');
|
||||
|
||||
$account->save();
|
||||
|
||||
Session::flash('message', trans('texts.updated_settings'));
|
||||
|
79
app/Http/Controllers/ClientAuth/AuthController.php
Normal file
79
app/Http/Controllers/ClientAuth/AuthController.php
Normal file
@ -0,0 +1,79 @@
|
||||
<?php namespace App\Http\Controllers\ClientAuth;
|
||||
|
||||
use Auth;
|
||||
use Event;
|
||||
use Utils;
|
||||
use Session;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\User;
|
||||
use App\Events\UserLoggedIn;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Ninja\Repositories\AccountRepository;
|
||||
use App\Services\AuthService;
|
||||
use App\Models\Invitation;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
|
||||
class AuthController extends Controller {
|
||||
|
||||
protected $guard = 'client';
|
||||
protected $redirectTo = '/client/dashboard';
|
||||
|
||||
use AuthenticatesUsers;
|
||||
|
||||
public function showLoginForm()
|
||||
{
|
||||
$data = array(
|
||||
);
|
||||
|
||||
$invitation_key = session('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$invoice = $invitation->invoice;
|
||||
$client = $invoice->client;
|
||||
$account = $client->account;
|
||||
|
||||
$data['hideLogo'] = $account->isWhiteLabel();
|
||||
$data['clientViewCSS'] = $account->clientViewCSS();
|
||||
$data['clientFontUrl'] = $account->getFontsUrl();
|
||||
}
|
||||
}
|
||||
|
||||
return view('clientauth.login')->with($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the needed authorization credentials from the request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return array
|
||||
*/
|
||||
protected function getCredentials(Request $request)
|
||||
{
|
||||
$credentials = $request->only('password');
|
||||
$credentials['id'] = null;
|
||||
|
||||
$invitation_key = session('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$credentials['id'] = $invitation->contact_id;
|
||||
}
|
||||
}
|
||||
|
||||
return $credentials;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the user login request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return void
|
||||
*/
|
||||
protected function validateLogin(Request $request)
|
||||
{
|
||||
$this->validate($request, [
|
||||
'password' => 'required',
|
||||
]);
|
||||
}
|
||||
}
|
197
app/Http/Controllers/ClientAuth/PasswordController.php
Normal file
197
app/Http/Controllers/ClientAuth/PasswordController.php
Normal file
@ -0,0 +1,197 @@
|
||||
<?php namespace App\Http\Controllers\ClientAuth;
|
||||
|
||||
use Config;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Foundation\Auth\ResetsPasswords;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Mail\Message;
|
||||
use Illuminate\Support\Facades\Password;
|
||||
use App\Models\Invitation;
|
||||
|
||||
|
||||
class PasswordController extends Controller {
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Password Reset Controller
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This controller is responsible for handling password reset requests
|
||||
| and uses a simple trait to include this behavior. You're free to
|
||||
| explore this trait and override any methods you wish to tweak.
|
||||
|
|
||||
*/
|
||||
|
||||
use ResetsPasswords;
|
||||
|
||||
protected $redirectTo = '/client/dashboard';
|
||||
|
||||
/**
|
||||
* Create a new password controller instance.
|
||||
*
|
||||
* @param \Illuminate\Contracts\Auth\Guard $auth
|
||||
* @param \Illuminate\Contracts\Auth\PasswordBroker $passwords
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest');
|
||||
Config::set("auth.defaults.passwords","client");
|
||||
}
|
||||
|
||||
public function showLinkRequestForm()
|
||||
{
|
||||
$data = array();
|
||||
$invitation_key = session('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$invoice = $invitation->invoice;
|
||||
$client = $invoice->client;
|
||||
$account = $client->account;
|
||||
|
||||
$data['hideLogo'] = $account->isWhiteLabel();
|
||||
$data['clientViewCSS'] = $account->clientViewCSS();
|
||||
$data['clientFontUrl'] = $account->getFontsUrl();
|
||||
}
|
||||
}
|
||||
|
||||
return view('clientauth.password')->with($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a reset link to the given user.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function sendResetLinkEmail(Request $request)
|
||||
{
|
||||
$broker = $this->getBroker();
|
||||
|
||||
$contact_id = null;
|
||||
$invitation_key = session('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$contact_id = $invitation->contact_id;
|
||||
}
|
||||
}
|
||||
|
||||
$response = Password::broker($broker)->sendResetLink(array('id'=>$contact_id), function (Message $message) {
|
||||
$message->subject($this->getEmailSubject());
|
||||
});
|
||||
|
||||
switch ($response) {
|
||||
case Password::RESET_LINK_SENT:
|
||||
return $this->getSendResetLinkEmailSuccessResponse($response);
|
||||
|
||||
case Password::INVALID_USER:
|
||||
default:
|
||||
return $this->getSendResetLinkEmailFailureResponse($response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 $invitation_key
|
||||
* @param string|null $token
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function showResetForm(Request $request, $invitation_key = null, $token = null)
|
||||
{
|
||||
if (is_null($token)) {
|
||||
return $this->getEmail();
|
||||
}
|
||||
|
||||
$data = compact('token', 'invitation_key');
|
||||
$invitation_key = session('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$invoice = $invitation->invoice;
|
||||
$client = $invoice->client;
|
||||
$account = $client->account;
|
||||
|
||||
$data['hideLogo'] = $account->isWhiteLabel();
|
||||
$data['clientViewCSS'] = $account->clientViewCSS();
|
||||
$data['clientFontUrl'] = $account->getFontsUrl();
|
||||
}
|
||||
}
|
||||
|
||||
return view('clientauth.reset')->with($data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 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 $invitation_key
|
||||
* @param string|null $token
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function getReset(Request $request, $invitation_key = null, $token = null)
|
||||
{
|
||||
return $this->showResetForm($request, $invitation_key, $token);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the given user's password.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function reset(Request $request)
|
||||
{
|
||||
$this->validate($request, $this->getResetValidationRules());
|
||||
|
||||
$credentials = $request->only(
|
||||
'password', 'password_confirmation', 'token'
|
||||
);
|
||||
|
||||
$credentials['id'] = null;
|
||||
|
||||
$invitation_key = $request->input('invitation_key');
|
||||
if($invitation_key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $invitation_key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
$credentials['id'] = $invitation->contact_id;
|
||||
}
|
||||
}
|
||||
|
||||
$broker = $this->getBroker();
|
||||
|
||||
$response = Password::broker($broker)->reset($credentials, function ($user, $password) {
|
||||
$this->resetPassword($user, $password);
|
||||
});
|
||||
|
||||
switch ($response) {
|
||||
case Password::PASSWORD_RESET:
|
||||
return $this->getResetSuccessResponse($response);
|
||||
|
||||
default:
|
||||
return $this->getResetFailureResponse($request, $response);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the password reset validation rules.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getResetValidationRules()
|
||||
{
|
||||
return [
|
||||
'token' => 'required',
|
||||
'password' => 'required|confirmed|min:6',
|
||||
];
|
||||
}
|
||||
}
|
@ -1,28 +1,13 @@
|
||||
<?php namespace App\Http\Middleware;
|
||||
|
||||
use Closure;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Auth;
|
||||
use Session;
|
||||
use App\Models\Invitation;
|
||||
use App\Models\Contact;
|
||||
use App\Models\Account;
|
||||
|
||||
class Authenticate {
|
||||
|
||||
/**
|
||||
* The Guard implementation.
|
||||
*
|
||||
* @var Guard
|
||||
*/
|
||||
protected $auth;
|
||||
|
||||
/**
|
||||
* Create a new filter instance.
|
||||
*
|
||||
* @param Guard $auth
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Guard $auth)
|
||||
{
|
||||
$this->auth = $auth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
@ -30,9 +15,46 @@ class Authenticate {
|
||||
* @param \Closure $next
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next)
|
||||
public function handle($request, Closure $next, $guard = 'user')
|
||||
{
|
||||
if ($this->auth->guest())
|
||||
$authenticated = Auth::guard($guard)->check();
|
||||
|
||||
if($guard == 'client' && !empty($request->invitation_key)){
|
||||
$old_key = session('invitation_key');
|
||||
if($old_key && $old_key != $request->invitation_key){
|
||||
if($this->getInvitationContactId($old_key) != $this->getInvitationContactId($request->invitation_key)){
|
||||
// This is a different client; reauthenticate
|
||||
$authenticated = false;
|
||||
Auth::guard($guard)->logout();
|
||||
}
|
||||
}
|
||||
Session::put('invitation_key', $request->invitation_key);
|
||||
}
|
||||
|
||||
if($guard=='client'){
|
||||
$invitation_key = session('invitation_key');
|
||||
$account_id = $this->getInvitationAccountId($invitation_key);
|
||||
|
||||
if(Auth::guard('user')->check() && Auth::user('user')->account_id === $account_id){
|
||||
// This is an admin; let them pretend to be a client
|
||||
$authenticated = true;
|
||||
}
|
||||
|
||||
// Does this account require portal passwords?
|
||||
$account = Account::whereId($account_id)->first();
|
||||
if(!$account->enable_portal_password || !$account->isPro()){
|
||||
$authenticated = true;
|
||||
}
|
||||
|
||||
if(!$authenticated){
|
||||
$contact = Contact::whereId($this->getInvitationContactId($invitation_key))->first();
|
||||
if($contact && !$contact->password){
|
||||
$authenticated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$authenticated)
|
||||
{
|
||||
if ($request->ajax())
|
||||
{
|
||||
@ -40,11 +62,30 @@ class Authenticate {
|
||||
}
|
||||
else
|
||||
{
|
||||
return redirect()->guest('/login');
|
||||
return redirect()->guest($guard=='client'?'/client/login':'/login');
|
||||
}
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
protected function getInvitation($key){
|
||||
$invitation = Invitation::where('invitation_key', '=', $key)->first();
|
||||
if ($invitation && !$invitation->is_deleted) {
|
||||
return $invitation;
|
||||
}
|
||||
else return null;
|
||||
}
|
||||
|
||||
protected function getInvitationContactId($key){
|
||||
$invitation = $this->getInvitation($key);
|
||||
|
||||
return $invitation?$invitation->contact_id:null;
|
||||
}
|
||||
|
||||
protected function getInvitationAccountId($key){
|
||||
$invitation = $this->getInvitation($key);
|
||||
|
||||
return $invitation?$invitation->account_id:null;
|
||||
}
|
||||
}
|
||||
|
@ -35,17 +35,19 @@ Route::get('/keep_alive', 'HomeController@keepAlive');
|
||||
Route::post('/get_started', 'AccountController@getStarted');
|
||||
|
||||
// Client visible pages
|
||||
Route::get('view/{invitation_key}', 'PublicClientController@view');
|
||||
Route::get('download/{invitation_key}', 'PublicClientController@download');
|
||||
Route::get('view', 'HomeController@viewLogo');
|
||||
Route::get('approve/{invitation_key}', 'QuoteController@approve');
|
||||
Route::get('payment/{invitation_key}/{payment_type?}', 'PaymentController@show_payment');
|
||||
Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
|
||||
Route::get('complete', 'PaymentController@offsite_payment');
|
||||
Route::get('client/quotes', 'PublicClientController@quoteIndex');
|
||||
Route::get('client/invoices', 'PublicClientController@invoiceIndex');
|
||||
Route::get('client/payments', 'PublicClientController@paymentIndex');
|
||||
Route::get('client/dashboard', 'PublicClientController@dashboard');
|
||||
Route::group(['middleware' => 'auth:client'], function() {
|
||||
Route::get('view/{invitation_key}', 'PublicClientController@view');
|
||||
Route::get('download/{invitation_key}', 'PublicClientController@download');
|
||||
Route::get('view', 'HomeController@viewLogo');
|
||||
Route::get('approve/{invitation_key}', 'QuoteController@approve');
|
||||
Route::get('payment/{invitation_key}/{payment_type?}', 'PaymentController@show_payment');
|
||||
Route::post('payment/{invitation_key}', 'PaymentController@do_payment');
|
||||
Route::get('complete', 'PaymentController@offsite_payment');
|
||||
Route::get('client/quotes', 'PublicClientController@quoteIndex');
|
||||
Route::get('client/invoices', 'PublicClientController@invoiceIndex');
|
||||
Route::get('client/payments', 'PublicClientController@paymentIndex');
|
||||
Route::get('client/dashboard', 'PublicClientController@dashboard');
|
||||
});
|
||||
Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable'));
|
||||
Route::get('api/client.invoices', array('as'=>'api.client.invoices', 'uses'=>'PublicClientController@invoiceDatatable'));
|
||||
Route::get('api/client.payments', array('as'=>'api.client.payments', 'uses'=>'PublicClientController@paymentDatatable'));
|
||||
@ -76,6 +78,15 @@ Route::get('/password/reset/{token}', array('as' => 'forgot', 'uses' => 'Auth\Pa
|
||||
Route::post('/password/reset', array('as' => 'forgot', 'uses' => 'Auth\PasswordController@postReset'));
|
||||
Route::get('/user/confirm/{code}', 'UserController@confirm');
|
||||
|
||||
// Client auth
|
||||
Route::get('/client/login', array('as' => 'login', 'uses' => 'ClientAuth\AuthController@getLogin'));
|
||||
Route::post('/client/login', array('as' => 'login', 'uses' => 'ClientAuth\AuthController@postLogin'));
|
||||
Route::get('/client/logout', array('as' => 'logout', 'uses' => 'ClientAuth\AuthController@getLogout'));
|
||||
Route::get('/client/forgot', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@getEmail'));
|
||||
Route::post('/client/forgot', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@postEmail'));
|
||||
Route::get('/client/password/reset/{invitation_key}/{token}', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@getReset'));
|
||||
Route::post('/client/password/reset', array('as' => 'forgot', 'uses' => 'ClientAuth\PasswordController@postReset'));
|
||||
|
||||
|
||||
if (Utils::isNinja()) {
|
||||
Route::post('/signup/register', 'AccountController@doRegister');
|
||||
@ -87,7 +98,7 @@ if (Utils::isReseller()) {
|
||||
Route::post('/reseller_stats', 'AppController@stats');
|
||||
}
|
||||
|
||||
Route::group(['middleware' => 'auth'], function() {
|
||||
Route::group(['middleware' => 'auth:user'], function() {
|
||||
Route::get('dashboard', 'DashboardController@index');
|
||||
Route::get('view_archive/{entity_type}/{visible}', 'AccountController@setTrashVisible');
|
||||
Route::get('hide_message', 'HomeController@hideMessage');
|
||||
|
@ -155,6 +155,14 @@ class Client extends EntityModel
|
||||
$contact->send_invoice = true;
|
||||
}
|
||||
|
||||
if (!Utils::isPro() || $this->account->enable_portal_password){
|
||||
if(!empty($data['password']) && $data['password']!='-%unchanged%-'){
|
||||
$contact->password = bcrypt($data['password']);
|
||||
} else if(empty($data['password'])){
|
||||
$contact->password = null;
|
||||
}
|
||||
}
|
||||
|
||||
$contact->fill($data);
|
||||
$contact->is_primary = $isPrimary;
|
||||
|
||||
|
@ -3,10 +3,14 @@
|
||||
use HTML;
|
||||
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Auth\Authenticatable;
|
||||
use Illuminate\Auth\Passwords\CanResetPassword;
|
||||
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
|
||||
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;
|
||||
|
||||
class Contact extends EntityModel
|
||||
class Contact extends EntityModel implements AuthenticatableContract, CanResetPasswordContract
|
||||
{
|
||||
use SoftDeletes;
|
||||
use SoftDeletes, Authenticatable, CanResetPassword;
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
protected $fillable = [
|
||||
|
@ -27,6 +27,7 @@ class ContactMailer extends Mailer
|
||||
'firstName',
|
||||
'invoice',
|
||||
'quote',
|
||||
'password',
|
||||
'viewLink',
|
||||
'viewButton',
|
||||
'paymentLink',
|
||||
@ -110,6 +111,13 @@ class ContactMailer extends Mailer
|
||||
'amount' => $invoice->getRequestedAmount()
|
||||
];
|
||||
|
||||
if (empty($invitation->contact->password) && $account->isPro() && $account->enable_portal_password && $account->send_portal_password) {
|
||||
// The contact needs a password
|
||||
$variables['password'] = $password = $this->generatePassword();
|
||||
$invitation->contact->password = bcrypt($password);
|
||||
$invitation->contact->save();
|
||||
}
|
||||
|
||||
$data = [
|
||||
'body' => $this->processVariables($body, $variables),
|
||||
'link' => $invitation->getLink(),
|
||||
@ -144,6 +152,28 @@ class ContactMailer extends Mailer
|
||||
}
|
||||
}
|
||||
|
||||
protected function generatePassword($length = 9)
|
||||
{
|
||||
$sets = array(
|
||||
'abcdefghjkmnpqrstuvwxyz',
|
||||
'ABCDEFGHJKMNPQRSTUVWXYZ',
|
||||
'23456789',
|
||||
);
|
||||
$all = '';
|
||||
$password = '';
|
||||
foreach($sets as $set)
|
||||
{
|
||||
$password .= $set[array_rand(str_split($set))];
|
||||
$all .= $set;
|
||||
}
|
||||
$all = str_split($all);
|
||||
for($i = 0; $i < $length - count($sets); $i++)
|
||||
$password .= $all[array_rand($all)];
|
||||
$password = str_shuffle($password);
|
||||
|
||||
return $password;
|
||||
}
|
||||
|
||||
public function sendPaymentConfirmation(Payment $payment)
|
||||
{
|
||||
$account = $payment->account;
|
||||
@ -232,6 +262,7 @@ class ContactMailer extends Mailer
|
||||
$client = $data['client'];
|
||||
$invitation = $data['invitation'];
|
||||
$invoice = $invitation->invoice;
|
||||
$passwordHTML = isset($data['password'])?'<p>'.trans('texts.password').': '.$data['password'].'<p>':false;
|
||||
|
||||
$variables = [
|
||||
'$footer' => $account->getEmailFooter(),
|
||||
@ -245,10 +276,11 @@ class ContactMailer extends Mailer
|
||||
'$invoice' => $invoice->invoice_number,
|
||||
'$quote' => $invoice->invoice_number,
|
||||
'$link' => $invitation->getLink(),
|
||||
'$viewLink' => $invitation->getLink(),
|
||||
'$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()),
|
||||
'$paymentLink' => $invitation->getLink('payment'),
|
||||
'$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')),
|
||||
'$password' => $passwordHTML,
|
||||
'$viewLink' => $invitation->getLink().'$password',
|
||||
'$viewButton' => Form::emailViewButton($invitation->getLink(), $invoice->getEntityType()).'$password',
|
||||
'$paymentLink' => $invitation->getLink('payment').'$password',
|
||||
'$paymentButton' => Form::emailPaymentButton($invitation->getLink('payment')).'$password',
|
||||
'$customClient1' => $account->custom_client_label1,
|
||||
'$customClient2' => $account->custom_client_label2,
|
||||
'$customInvoice1' => $account->custom_invoice_text_label1,
|
||||
@ -263,9 +295,21 @@ class ContactMailer extends Mailer
|
||||
$variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}");
|
||||
}
|
||||
|
||||
$includesPasswordPlaceholder = strpos($template, '$password') !== false;
|
||||
|
||||
$str = str_replace(array_keys($variables), array_values($variables), $template);
|
||||
$str = autolink($str, 100);
|
||||
|
||||
if(!$includesPasswordPlaceholder && $passwordHTML){
|
||||
$pos = strrpos($str, '$password');
|
||||
if($pos !== false)
|
||||
{
|
||||
$str = substr_replace($str, $passwordHTML, $pos, 9/* length of "$password" */);
|
||||
}
|
||||
}
|
||||
$str = str_replace('$password', '', $str);
|
||||
|
||||
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Authentication Defaults
|
||||
@ -14,7 +13,7 @@ return [
|
||||
*/
|
||||
|
||||
'defaults' => [
|
||||
'guard' => 'web',
|
||||
'guard' => 'user',
|
||||
'passwords' => 'users',
|
||||
],
|
||||
|
||||
@ -36,11 +35,16 @@ return [
|
||||
*/
|
||||
|
||||
'guards' => [
|
||||
'web' => [
|
||||
'user' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'users',
|
||||
],
|
||||
|
||||
'client' => [
|
||||
'driver' => 'session',
|
||||
'provider' => 'client',
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'driver' => 'token',
|
||||
'provider' => 'users',
|
||||
@ -70,10 +74,10 @@ return [
|
||||
'model' => App\Models\User::class,
|
||||
],
|
||||
|
||||
// 'users' => [
|
||||
// 'driver' => 'database',
|
||||
// 'table' => 'users',
|
||||
// ],
|
||||
'client' => [
|
||||
'driver' => 'eloquent',
|
||||
'model' => App\Models\Contact::class,
|
||||
]
|
||||
],
|
||||
|
||||
/*
|
||||
@ -98,7 +102,13 @@ return [
|
||||
'passwords' => [
|
||||
'users' => [
|
||||
'provider' => 'users',
|
||||
'email' => 'emails.password', //auth.emails.password
|
||||
'email' => 'emails.password',
|
||||
'table' => 'password_resets',
|
||||
'expire' => 60,
|
||||
],
|
||||
'client' => [
|
||||
'provider' => 'client',
|
||||
'email' => 'emails.client_password',
|
||||
'table' => 'password_resets',
|
||||
'expire' => 60,
|
||||
],
|
||||
|
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
|
||||
class AddClientPassword extends Migration {
|
||||
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->boolean('enable_portal_password')->default(0);
|
||||
$table->boolean('send_portal_password')->default(0);
|
||||
});
|
||||
|
||||
Schema::table('contacts', function ($table) {
|
||||
$table->string('password', 255)->nullable();
|
||||
$table->boolean('confirmation_code', 255)->nullable();
|
||||
$table->boolean('remember_token', 100)->nullable();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('accounts', function ($table) {
|
||||
$table->dropColumn('enable_portal_password');
|
||||
$table->dropColumn('send_portal_password');
|
||||
});
|
||||
|
||||
Schema::table('contacts', function ($table) {
|
||||
$table->dropColumn('password');
|
||||
$table->dropColumn('confirmation_code');
|
||||
$table->dropColumn('remember_token');
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -1052,6 +1052,11 @@ $LANG = array(
|
||||
'enable_client_portal' => 'Dashboard',
|
||||
'enable_client_portal_help' => 'Show/hide the dashboard page in the client portal.',
|
||||
|
||||
// Client Passwords
|
||||
'enable_portal_password'=>'Password protect invoices',
|
||||
'enable_portal_password_help'=>'Allows you to set a password for each contact. If a password is set, the contact will be required to enter a password before viewing invoices.',
|
||||
'send_portal_password'=>'Generate password automatically',
|
||||
'send_portal_password_help'=>'If no password is set, one will be generated and sent with the first invoice.',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -1,69 +1,86 @@
|
||||
@extends('header')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
@parent
|
||||
|
||||
<link href='https://fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'>
|
||||
<link href='https://fonts.googleapis.com/css?family=Roboto+Mono' rel='stylesheet' type='text/css'>
|
||||
@stop
|
||||
|
||||
@section('content')
|
||||
@parent
|
||||
@parent
|
||||
|
||||
{!! Former::open_for_files()
|
||||
->addClass('warn-on-exit') !!}
|
||||
{!! Former::open_for_files()
|
||||
->addClass('warn-on-exit') !!}
|
||||
|
||||
{!! Former::populateField('enable_client_portal', intval($account->enable_client_portal)) !!}
|
||||
{!! Former::populateField('client_view_css', $client_view_css) !!}
|
||||
|
||||
@if (!Utils::isNinja() && !Auth::user()->account->isWhiteLabel())
|
||||
<div class="alert alert-warning" style="font-size:larger;">
|
||||
<center>
|
||||
{!! trans('texts.white_label_custom_css', ['link'=>'<a href="#" onclick="$(\'#whiteLabelModal\').modal(\'show\');">'.trans('texts.white_label_purchase_link').'</a>']) !!}
|
||||
</center>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@include('accounts.nav', ['selected' => ACCOUNT_CLIENT_PORTAL])
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.client_portal') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
{!! Former::checkbox('enable_client_portal')
|
||||
->text(trans('texts.enable'))
|
||||
->help(trans('texts.enable_client_portal_help')) !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.custom_css') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
{!! Former::textarea('client_view_css')
|
||||
->label(trans('texts.custom_css'))
|
||||
->rows(10)
|
||||
->raw()
|
||||
->autofocus()
|
||||
->maxlength(60000)
|
||||
->style("min-width:100%;max-width:100%;font-family:'Roboto Mono', 'Lucida Console', Monaco, monospace;font-size:14px;'") !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{!! Former::populateField('enable_client_portal', intval($account->enable_client_portal)) !!}
|
||||
{!! Former::populateField('client_view_css', $client_view_css) !!}
|
||||
{!! Former::populateField('enable_portal_password', $enable_portal_password) !!}
|
||||
{!! Former::populateField('send_portal_password', $send_portal_password) !!}
|
||||
|
||||
@if (!Utils::isNinja() && !Auth::user()->account->isWhiteLabel())
|
||||
<div class="alert alert-warning" style="font-size:larger;">
|
||||
<center>
|
||||
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
|
||||
{!! trans('texts.white_label_custom_css', ['link'=>'<a href="#" onclick="$(\'#whiteLabelModal\').modal(\'show\');">'.trans('texts.white_label_purchase_link').'</a>']) !!}
|
||||
</center>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{!! Former::close() !!}
|
||||
@include('accounts.nav', ['selected' => ACCOUNT_CLIENT_PORTAL])
|
||||
|
||||
<div class="row">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.client_portal') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
{!! Former::checkbox('enable_client_portal')
|
||||
->text(trans('texts.enable'))
|
||||
->help(trans('texts.enable_client_portal_help')) !!}
|
||||
</div>
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
{!! Former::checkbox('enable_portal_password')
|
||||
->text(trans('texts.enable_portal_password'))
|
||||
->help(trans('texts.enable_portal_password_help'))
|
||||
->label(' ') !!}
|
||||
</div>
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
{!! Former::checkbox('send_portal_password')
|
||||
->text(trans('texts.send_portal_password'))
|
||||
->help(trans('texts.send_portal_password_help'))
|
||||
->label(' ') !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title">{!! trans('texts.custom_css') !!}</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<div class="col-md-10 col-md-offset-1">
|
||||
{!! Former::textarea('client_view_css')
|
||||
->label(trans('texts.custom_css'))
|
||||
->rows(10)
|
||||
->raw()
|
||||
->autofocus()
|
||||
->maxlength(60000)
|
||||
->style("min-width:100%;max-width:100%;font-family:'Roboto Mono', 'Lucida Console', Monaco, monospace;font-size:14px;'") !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<center>
|
||||
{!! Button::success(trans('texts.save'))->submit()->large()->appendIcon(Icon::create('floppy-disk')) !!}
|
||||
</center>
|
||||
|
||||
{!! Former::close() !!}
|
||||
<script>
|
||||
$('#enable_portal_password').change(fixCheckboxes);
|
||||
function fixCheckboxes(){
|
||||
var checked = $('#enable_portal_password').is(':checked');
|
||||
$('#send_portal_password').prop('disabled', !checked);
|
||||
}
|
||||
fixCheckboxes();
|
||||
</script>
|
||||
@stop
|
@ -200,6 +200,7 @@
|
||||
}
|
||||
|
||||
var keys = {!! json_encode(\App\Ninja\Mailers\ContactMailer::$variableFields) !!};
|
||||
var passwordHtml = "{!! $account->isPro() && $account->enable_portal_password && $account->send_portal_password?'<p>'.trans('texts.password').': 6h2NWNdw6<p>':'' !!}";
|
||||
var vals = [
|
||||
{!! json_encode($emailFooter) !!},
|
||||
"{{ $account->getDisplayName() }}",
|
||||
@ -211,10 +212,11 @@
|
||||
"First Name",
|
||||
"0001",
|
||||
"0001",
|
||||
"{{ URL::to('/view/...') }}",
|
||||
'{!! Form::flatButton('view_invoice', '#0b4d78') !!}',
|
||||
"{{ URL::to('/payment/...') }}",
|
||||
'{!! Form::flatButton('pay_now', '#36c157') !!}',
|
||||
passwordHtml,
|
||||
"{{ URL::to('/view/...') }}$password",
|
||||
'{!! Form::flatButton('view_invoice', '#0b4d78') !!}$password',
|
||||
"{{ URL::to('/payment/...') }}$password",
|
||||
'{!! Form::flatButton('pay_now', '#36c157') !!}$password',
|
||||
];
|
||||
|
||||
// Add blanks for custom values
|
||||
@ -230,11 +232,19 @@
|
||||
{!! "vals.push('" . Form::flatButton('pay_now', '#36c157') . "');" !!}
|
||||
@endforeach
|
||||
|
||||
var includesPasswordPlaceholder = str.indexOf('$password') != -1;
|
||||
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
var regExp = new RegExp('\\$'+keys[i], 'g');
|
||||
str = str.replace(regExp, vals[i]);
|
||||
}
|
||||
|
||||
if(!includesPasswordPlaceholder){
|
||||
var lastSpot = str.lastIndexOf('$password')
|
||||
str = str.slice(0, lastSpot) + str.slice(lastSpot).replace('$password', passwordHtml);
|
||||
}
|
||||
str = str.replace(/\$password/g,'');
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,9 @@
|
||||
{!! Former::text('last_name')->data_bind("value: last_name, valueUpdate: 'afterkeydown'") !!}
|
||||
{!! Former::text('email')->data_bind("value: email, valueUpdate: 'afterkeydown'") !!}
|
||||
{!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown'") !!}
|
||||
@if ($account->isPro() && $account->enable_portal_password)
|
||||
{!! Former::password('password')->data_bind("value: password()?'-%unchanged%-':'', valueUpdate: 'afterkeydown'") !!}
|
||||
@endif
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-lg-8 col-lg-offset-4">
|
||||
|
120
resources/views/clientauth/login.blade.php
Normal file
120
resources/views/clientauth/login.blade.php
Normal file
@ -0,0 +1,120 @@
|
||||
@extends('public.header')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.modal-header {
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
background:#222;
|
||||
color:#fff
|
||||
}
|
||||
.modal-header h4 {
|
||||
margin:0;
|
||||
}
|
||||
.modal-header img {
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.form-signin {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
}
|
||||
p.link a {
|
||||
font-size: 11px;
|
||||
}
|
||||
.form-signin .inner {
|
||||
padding: 20px;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-left: 1px solid #ddd;
|
||||
border-right: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.form-signin .checkbox {
|
||||
font-weight: normal;
|
||||
}
|
||||
.form-signin .form-control {
|
||||
margin-bottom: 17px !important;
|
||||
}
|
||||
.form-signin .form-control:focus {
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.modal-header a:link,
|
||||
.modal-header a:visited,
|
||||
.modal-header a:hover,
|
||||
.modal-header a:active {
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
@endsection
|
||||
|
||||
@section('body')
|
||||
<div class="container">
|
||||
|
||||
@include('partials.warn_session', ['redirectTo' => '/client/login'])
|
||||
|
||||
{!! Former::open('client/login')
|
||||
->rules(['password' => 'required'])
|
||||
->addClass('form-signin') !!}
|
||||
{{ Former::populateField('remember', 'true') }}
|
||||
|
||||
<div class="modal-header">
|
||||
@if (!isset($hideLogo) || !$hideLogo)
|
||||
<a href="{{ NINJA_WEB_URL }}" target="_blank">
|
||||
<img src="{{ asset('images/icon-login.png') }}" />
|
||||
<h4>Invoice Ninja | {{ trans('texts.account_login') }}</h4>
|
||||
</a>
|
||||
@else
|
||||
<h4>{{ trans('texts.account_login') }}</h4>
|
||||
@endif
|
||||
</div>
|
||||
<div class="inner">
|
||||
<p>
|
||||
{!! Former::password('password')->placeholder(trans('texts.password'))->raw() !!}
|
||||
{!! Former::hidden('remember')->raw() !!}
|
||||
</p>
|
||||
|
||||
<p>{!! Button::success(trans('texts.login'))
|
||||
->withAttributes(['id' => 'loginButton'])
|
||||
->large()->submit()->block() !!}</p>
|
||||
|
||||
<p class="link">
|
||||
{!! link_to('/client/forgot', trans('texts.forgot_password')) !!}
|
||||
</p>
|
||||
|
||||
|
||||
@if (count($errors->all()))
|
||||
<div class="alert alert-danger">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (Session::has('warning'))
|
||||
<div class="alert alert-warning">{{ Session::get('warning') }}</div>
|
||||
@endif
|
||||
|
||||
@if (Session::has('message'))
|
||||
<div class="alert alert-info">{{ Session::get('message') }}</div>
|
||||
@endif
|
||||
|
||||
@if (Session::has('error'))
|
||||
<div class="alert alert-danger"><li>{{ Session::get('error') }}</li></div>
|
||||
@endif
|
||||
|
||||
</div>
|
||||
|
||||
{!! Former::close() !!}
|
||||
</div>
|
||||
@endsection
|
108
resources/views/clientauth/password.blade.php
Normal file
108
resources/views/clientauth/password.blade.php
Normal file
@ -0,0 +1,108 @@
|
||||
@extends('public.header')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.modal-header {
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
background:#222;
|
||||
color:#fff
|
||||
}
|
||||
.modal-header h4 {
|
||||
margin:0;
|
||||
}
|
||||
.modal-header img {
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.form-signin {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
}
|
||||
p.link a {
|
||||
font-size: 11px;
|
||||
}
|
||||
.form-signin .inner {
|
||||
padding: 20px;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-left: 1px solid #ddd;
|
||||
border-right: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.form-signin .checkbox {
|
||||
font-weight: normal;
|
||||
}
|
||||
.form-signin .form-control {
|
||||
margin-bottom: 17px !important;
|
||||
}
|
||||
.form-signin .form-control:focus {
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
||||
|
||||
@stop
|
||||
|
||||
@section('body')
|
||||
<div class="container">
|
||||
|
||||
{!! Former::open('client/forgot')->addClass('form-signin') !!}
|
||||
<div class="modal-header">
|
||||
@if (!isset($hideLogo) || !$hideLogo)
|
||||
<a href="{{ NINJA_WEB_URL }}" target="_blank">
|
||||
<img src="{{ asset('images/icon-login.png') }}" />
|
||||
<h4>Invoice Ninja | {{ trans('texts.password_recovery') }}</h4>
|
||||
</a>
|
||||
@else
|
||||
<h4>{{ trans('texts.password_recovery') }}</h4>
|
||||
@endif
|
||||
</div>
|
||||
<div class="inner">
|
||||
|
||||
<p>{!! Button::success(trans('texts.send_email'))->large()->submit()->block() !!}</p>
|
||||
|
||||
@if (count($errors->all()))
|
||||
<div class="alert alert-danger">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if (session('status'))
|
||||
<div class="alert alert-info">
|
||||
{{ session('status') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- if there are login errors, show them here -->
|
||||
@if (Session::has('warning'))
|
||||
<div class="alert alert-warning">{{ Session::get('warning') }}</div>
|
||||
@endif
|
||||
|
||||
@if (Session::has('message'))
|
||||
<div class="alert alert-info">{{ Session::get('message') }}</div>
|
||||
@endif
|
||||
|
||||
@if (Session::has('error'))
|
||||
<div class="alert alert-danger">{{ Session::get('error') }}</div>
|
||||
@endif
|
||||
|
||||
{!! Former::close() !!}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(function() {
|
||||
$('#email').focus();
|
||||
})
|
||||
</script>
|
||||
|
||||
@stop
|
111
resources/views/clientauth/reset.blade.php
Normal file
111
resources/views/clientauth/reset.blade.php
Normal file
@ -0,0 +1,111 @@
|
||||
@extends('public.header')
|
||||
|
||||
@section('head')
|
||||
@parent
|
||||
<style type="text/css">
|
||||
body {
|
||||
padding-top: 40px;
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.modal-header {
|
||||
border-top-left-radius: 3px;
|
||||
border-top-right-radius: 3px;
|
||||
background:#222;
|
||||
color:#fff
|
||||
}
|
||||
.modal-header h4 {
|
||||
margin:0;
|
||||
}
|
||||
.modal-header img {
|
||||
float: left;
|
||||
margin-right: 20px;
|
||||
}
|
||||
.form-signin {
|
||||
max-width: 400px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
}
|
||||
p.link a {
|
||||
font-size: 11px;
|
||||
}
|
||||
.form-signin .inner {
|
||||
padding: 20px;
|
||||
border-bottom-right-radius: 3px;
|
||||
border-bottom-left-radius: 3px;
|
||||
border-left: 1px solid #ddd;
|
||||
border-right: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
.form-signin .checkbox {
|
||||
font-weight: normal;
|
||||
}
|
||||
.form-signin .form-control {
|
||||
margin-bottom: 17px !important;
|
||||
}
|
||||
.form-signin .form-control:focus {
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
||||
|
||||
@stop
|
||||
|
||||
@section('body')
|
||||
<div class="container">
|
||||
|
||||
{!! Former::open('/client/password/reset')->addClass('form-signin')->rules(array(
|
||||
'password' => 'required',
|
||||
'password_confirmation' => 'required',
|
||||
)) !!}
|
||||
|
||||
<div class="modal-header">
|
||||
@if (!isset($hideLogo) || !$hideLogo)
|
||||
<a href="{{ NINJA_WEB_URL }}" target="_blank">
|
||||
<img src="{{ asset('images/icon-login.png') }}" />
|
||||
<h4>Invoice Ninja | {{ trans('texts.set_password') }}</h4>
|
||||
</a>
|
||||
@else
|
||||
<h4>{{ trans('texts.set_password') }}</h4>
|
||||
@endif
|
||||
</div>
|
||||
<div class="inner">
|
||||
|
||||
<input type="hidden" name="token" value="{{{ $token }}}">
|
||||
<input type="hidden" name="invitation_key" value="{{{ $invitation_key }}}">
|
||||
|
||||
<p>
|
||||
{!! Former::password('password')->placeholder(trans('texts.password'))->raw() !!}
|
||||
{!! Former::password('password_confirmation')->placeholder(trans('texts.confirm_password'))->raw() !!}
|
||||
|
||||
</p>
|
||||
|
||||
<p>{!! Button::success(trans('texts.save'))->large()->submit()->block() !!}</p>
|
||||
|
||||
|
||||
@if (count($errors->all()))
|
||||
<div class="alert alert-danger">
|
||||
@foreach ($errors->all() as $error)
|
||||
<li>{{ $error }}</li>
|
||||
@endforeach
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<!-- if there are login errors, show them here -->
|
||||
@if (Session::has('warning'))
|
||||
<div class="alert alert-warning">{{ Session::get('warning') }}</div>
|
||||
@endif
|
||||
|
||||
@if (Session::has('message'))
|
||||
<div class="alert alert-info">{{ Session::get('message') }}</div>
|
||||
@endif
|
||||
|
||||
@if (Session::has('error'))
|
||||
<div class="alert alert-danger">{{ Session::get('error') }}</div>
|
||||
@endif
|
||||
|
||||
|
||||
{!! Former::close() !!}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@stop
|
@ -93,7 +93,10 @@
|
||||
attr: {name: 'contacts[' + \$index() + '][email]', id:'email'+\$index()}") !!}
|
||||
{!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown',
|
||||
attr: {name: 'contacts[' + \$index() + '][phone]'}") !!}
|
||||
|
||||
@if ($account->isPro() && $account->enable_portal_password)
|
||||
{!! Former::password('password')->data_bind("value: password()?'-%unchanged%-':'', valueUpdate: 'afterkeydown',
|
||||
attr: {name: 'contacts[' + \$index() + '][password]'}") !!}
|
||||
@endif
|
||||
<div class="form-group">
|
||||
<div class="col-lg-8 col-lg-offset-4 bold">
|
||||
<span class="redlink bold" data-bind="visible: $parent.contacts().length > 1">
|
||||
|
26
resources/views/emails/client_password.blade.php
Normal file
26
resources/views/emails/client_password.blade.php
Normal file
@ -0,0 +1,26 @@
|
||||
@extends('emails.master_user')
|
||||
|
||||
@section('body')
|
||||
<div>
|
||||
{{ trans('texts.reset_password') }}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<center>
|
||||
@include('partials.email_button', [
|
||||
'link' => URL::to("client/password/reset/".session('invitation_key')."/{$token}"),
|
||||
'field' => 'reset',
|
||||
'color' => '#36c157',
|
||||
])
|
||||
</center>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ trans('texts.email_signature') }}<br/>
|
||||
{{ trans('texts.email_from') }}
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{{ trans('texts.reset_password_footer') }}
|
||||
</div>
|
||||
@stop
|
@ -543,7 +543,10 @@
|
||||
->addClass('client-email') !!}
|
||||
{!! Former::text('phone')->data_bind("value: phone, valueUpdate: 'afterkeydown',
|
||||
attr: {name: 'client[contacts][' + \$index() + '][phone]'}") !!}
|
||||
|
||||
@if ($account->isPro() && $account->enable_portal_password)
|
||||
{!! Former::password('password')->data_bind("value: (typeof password=='function'?password():null)?'-%unchanged%-':'', valueUpdate: 'afterkeydown',
|
||||
attr: {name: 'client[contacts][' + \$index() + '][password]'}") !!}
|
||||
@endif
|
||||
<div class="form-group">
|
||||
<div class="col-lg-8 col-lg-offset-4">
|
||||
<span class="redlink bold" data-bind="visible: $parent.contacts().length > 1">
|
||||
|
Loading…
Reference in New Issue
Block a user