Template Controller (#3042)

* Request Cancellation

* Add fields to settings

* Recurring invoice cancellation request

* Stub Template controller
This commit is contained in:
David Bomba 2019-11-05 07:50:10 +11:00 committed by GitHub
parent b7d3f4e7aa
commit 9050d4e564
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
14 changed files with 405 additions and 57 deletions

View File

@ -169,6 +169,36 @@ class CompanySettings extends BaseSettings
public $email_signature = '';
public $enable_email_markup = true;
public $email_subject_custom1 = '';
public $email_subject_custom2 = '';
public $email_subject_custom3 = '';
public $email_template_custom1 = '';
public $email_template_custom2 = '';
public $email_template_custom3 = '';
public $enable_reminder1 = false;
public $enable_reminder2 = false;
public $enable_reminder3 = false;
public $num_days_reminder1 = 0;
public $num_days_reminder2 = 0;
public $num_days_reminder3 = 0;
public $schedule_reminder1 = ''; // (enum: after_invoice_date, before_due_date, after_due_date)
public $schedule_reminder2 = ''; // (enum: after_invoice_date, before_due_date, after_due_date)
public $schedule_reminder3 = ''; // (enum: after_invoice_date, before_due_date, after_due_date)
public $late_fee_amount1 = '';
public $late_fee_amount2 = '';
public $late_fee_amount3 = '';
public $endless_reminder_frequency_id = '0';
public $client_online_payment_notification = true;
public $client_manual_payment_notification = true;
/* Company Meta data that we can use to build sub companies*/
public $name = '';
@ -187,8 +217,8 @@ class CompanySettings extends BaseSettings
public $page_size = 'A4';
public $font_size = 9;
public $primary_font = 'roboto';
public $secondary_font = 'roboto';
public $primary_font = 'Roboto';
public $secondary_font = 'Roboto';
public $hide_paid_to_date = false;
public $embed_documents = false;
public $all_pages_header = true;
@ -196,6 +226,27 @@ class CompanySettings extends BaseSettings
public static $casts = [
'email_subject_custom1' => 'string',
'email_subject_custom2' => 'string',
'email_subject_custom3' => 'string',
'email_template_custom1' => 'string',
'email_template_custom2' => 'string',
'email_template_custom3' => 'string',
'enable_reminder1' => 'bool',
'enable_reminder2' => 'bool',
'enable_reminder3' => 'bool',
'num_days_reminder1' => 'int',
'num_days_reminder2' => 'int',
'num_days_reminder3' => 'int',
'schedule_reminder1' => 'string', // (enum: after_invoice_date, before_due_date, after_due_date)
'schedule_reminder2' => 'string', // (enum: after_invoice_date, before_due_date, after_due_date)
'schedule_reminder3' => 'string', // (enum: after_invoice_date, before_due_date, after_due_date)
'late_fee_amount1' => 'string',
'late_fee_amount2' => 'string',
'late_fee_amount3' => 'string',
'endless_reminder_frequency_id' => 'integer',
'client_online_payment_notification' => 'bool',
'client_manual_payment_notification' => 'bool',
'document_email_attachment' => 'bool',
'enable_client_portal_password' => 'bool',
'enable_email_markup' => 'bool',

View File

@ -27,7 +27,7 @@ class UserFactory
$user->failed_logins = 0;
$user->signature = '';
$user->theme_id = 0;
return $user;
}
}

View File

@ -63,13 +63,13 @@ class ContactLoginController extends Controller
public function authenticated(Request $request, ClientContact $client)
{
Auth::guard('contact')->login($client, true);
if(session()->get('url.intended'))
return redirect(session()->get('url.intended'));
return redirect()->intended();
return redirect(route('client.dashboard'));
}
public function logout()

View File

@ -36,8 +36,6 @@ class InvitationController extends Controller
if($invitation){
\Log::error("bool val = ".boolval($invitation->contact->client->getSetting('enable_client_portal_password')));
if((bool)$invitation->contact->client->getSetting('enable_client_portal_password') !== false)
$this->middleware('auth:contact');

View File

@ -14,10 +14,13 @@ namespace App\Http\Controllers\ClientPortal;
use App\Http\Controllers\Controller;
use App\Http\Requests\ClientPortal\ShowRecurringInvoiceRequest;
use App\Models\RecurringInvoice;
use App\Notifications\ClientContactRequestCancellation;
use App\Notifications\ClientContactResetPassword;
use App\Utils\Traits\MakesHash;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Notification;
use Yajra\DataTables\Facades\DataTables;
use Yajra\DataTables\Html\Builder;
@ -93,9 +96,8 @@ class RecurringInvoiceController extends Controller
];
//todo double check the user is able to request a cancellation
Mail::to(config('ninja.contact.ninja_official_contact'))
->send(new RecurringCancellationRequest($invoice));
//can add locale specific by chaining ->locale();
$recurring_invoice->user->notify(new ClientContactRequestCancellation($recurring_invoice, auth()->user()));
return view('portal.default.recurring_invoices.request_cancellation', $data);

View File

@ -139,11 +139,31 @@
* @OA\Property(property="enable_email_markup", type="boolean", example=false, description="____________"),
* @OA\Property(property="enable_client_portal_dashboard", type="boolean", example=false, description="____________"),
* @OA\Property(property="enable_client_portal", type="boolean", example=false, description="____________"),
* @OA\Property(property="email_template_statement", type="string", example='template matter', description="____________"),
* @OA\Property(property="email_subject_statement", type="string", example='subject matter', description="____________"),
* @OA\Property(property="email_template_statement", type="string", example="template matter", description="____________"),
* @OA\Property(property="email_subject_statement", type="string", example="subject matter", description="____________"),
* @OA\Property(property="signature_on_pdf", type="boolean", example=false, description="____________"),
* @OA\Property(property="send_portal_password", type="boolean", example=false, description="____________"),
* @OA\Property(property="quote_footer", type="string", example='the quote footer', description="____________"),
* @OA\Property(property="quote_footer", type="string", example="the quote footer", description="____________"),
* @OA\Property(property="email_subject_custom1", type="string", example="Custom Subject 1", description="____________"),
* @OA\Property(property="email_subject_custom2", type="string", example="Custom Subject 2", description="____________"),
* @OA\Property(property="email_subject_custom3", type="string", example="Custom Subject 3", description="____________"),
* @OA\Property(property="email_template_custom1", type="string", example="<HTML>", description="____________"),
* @OA\Property(property="email_template_custom2", type="string", example="<HTML>", description="____________"),
* @OA\Property(property="email_template_custom3", type="string", example="<HTML>", description="____________"),
* @OA\Property(property="enable_reminder1", type="boolean", example=false, description="____________"),
* @OA\Property(property="enable_reminder2", type="boolean", example=false, description="____________"),
* @OA\Property(property="enable_reminder3", type="boolean", example=false, description="____________"),
* @OA\Property(property="num_days_reminder1", type="number", example="9", description="The Reminder interval"),
* @OA\Property(property="num_days_reminder2", type="number", example="9", description="The Reminder interval"),
* @OA\Property(property="num_days_reminder3", type="number", example="9", description="The Reminder interval"),
* @OA\Property(property="schedule_reminder1", type="string", example="after_invoice_date", description="(enum: after_invoice_date, before_due_date, after_due_date)"),
* @OA\Property(property="schedule_reminder2", type="string", example="after_invoice_date", description="(enum: after_invoice_date, before_due_date, after_due_date)"),
* @OA\Property(property="schedule_reminder3", type="string", example="after_invoice_date", description="(enum: after_invoice_date, before_due_date, after_due_date)"),
* @OA\Property(property="late_fee_amount1", type="string", example="10.00", description="____________"),
* @OA\Property(property="late_fee_amount2", type="string", example="20.00", description="____________"),
* @OA\Property(property="late_fee_amount3", type="string", example="100.00", description="____________"),
* @OA\Property(property="endless_reminder_frequency_id", type="string", example="1", description="____________"),
* @OA\Property(property="client_online_payment_notification", type="boolean", example=false, description="____________"),
* @OA\Property(property="client_manual_payment_notification", type="boolean", example=false, description="____________"),
* )
*/
*/

View File

@ -0,0 +1,8 @@
<?php
/**
* @OA\Schema(
* schema="Template",
* type="object",
* @OA\Property(property="html", type="string", example="<HTML></HTML>", description="The template HTML"),
* )
*/

View File

@ -0,0 +1,135 @@
<?php
/**
* Invoice Ninja (https://invoiceninja.com)
*
* @link https://github.com/invoiceninja/invoiceninja source repository
*
* @copyright Copyright (c) 2019. Invoice Ninja LLC (https://invoiceninja.com)
*
* @license https://opensource.org/licenses/AAL
*/
namespace App\Http\Controllers;
class TemplateController extends BaseController
{
public function __construct()
{
parent::__construct();
}
/**
* Returns a blank entity template
*
* @return \Illuminate\Http\Response
*
* @OA\Get(
* path="/api/v1/templates/{entity}/create",
* operationId="getCreateTemplate",
* tags={"templates"},
* summary="Returns a blank entity template",
* description="Returns a blank HTML entity temlpate",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(
* name="entity",
* in="path",
* description="The Entity (invoice,quote,recurring_invoice)",
* example="invoice",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="The template response",
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Template"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function create($entity)
{
return response()->json(request()->all(), 200);
}
/**
* Returns a template filled with entity variables
*
* @return \Illuminate\Http\Response
*
* @OA\Get(
* path="/api/v1/templates/{entity}/{entity_id}",
* operationId="getShowTemplate",
* tags={"templates"},
* summary="Returns a entity template with the template variables replaced with the Entities",
* description="Returns a blank HTML entity temlpate",
* @OA\Parameter(ref="#/components/parameters/X-Api-Secret"),
* @OA\Parameter(ref="#/components/parameters/X-Requested-With"),
* @OA\Parameter(
* name="entity",
* in="path",
* description="The Entity (invoice,quote,recurring_invoice)",
* example="invoice",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Parameter(
* name="entity_id",
* in="path",
* description="The Entity ID",
* example="X9f87dkf",
* required=true,
* @OA\Schema(
* type="string",
* format="string",
* ),
* ),
* @OA\Response(
* response=200,
* description="The template response",
* @OA\Header(header="X-API-Version", ref="#/components/headers/X-API-Version"),
* @OA\Header(header="X-RateLimit-Remaining", ref="#/components/headers/X-RateLimit-Remaining"),
* @OA\Header(header="X-RateLimit-Limit", ref="#/components/headers/X-RateLimit-Limit"),
* @OA\JsonContent(ref="#/components/schemas/Template"),
* ),
* @OA\Response(
* response=422,
* description="Validation error",
* @OA\JsonContent(ref="#/components/schemas/ValidationError"),
* ),
* @OA\Response(
* response="default",
* description="Unexpected Error",
* @OA\JsonContent(ref="#/components/schemas/Error"),
* ),
* )
*/
public function show($entity, $entity_id)
{
return response()->json(request()->all(), 200);
}
}

View File

@ -1,35 +0,0 @@
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class RecurringCancellationRequest extends Mailable
{
use Queueable, SerializesModels;
/**
* Create a new message instance.
*
* @return void
*/
public $recurring_invoice
public function __construct(RecurringInvoice $recurring_invoice)
{
$this->recurring_invoice = $recurring_invoice
}
/**
* Build the message.
*
* @return $this
*/
public function build()
{
return $this->view('view.name');
}
}

View File

@ -27,10 +27,11 @@ use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;
use Laracasts\Presenter\PresentableTrait;
class User extends Authenticatable implements MustVerifyEmail,HasLocalePreference
class User extends Authenticatable implements MustVerifyEmail
{
use Notifiable;
use SoftDeletes;
@ -140,6 +141,7 @@ class User extends Authenticatable implements MustVerifyEmail,HasLocalePreferenc
*/
public function setCompany($company)
{
\Log::error('setting company');
$this->company = $company;
}
@ -161,6 +163,14 @@ class User extends Authenticatable implements MustVerifyEmail,HasLocalePreferenc
return $this->getCompany();
}
private function setCompanyByGuard()
{
if(Auth::guard('contact')->check())
$this->setCompany(auth()->user()->client->company);
}
/**
* Returns the pivot tables for Company / User
*
@ -319,6 +329,7 @@ class User extends Authenticatable implements MustVerifyEmail,HasLocalePreferenc
public function getEmailVerifiedAt()
{
if($this->email_verified_at)
return Carbon::parse($this->email_verified_at)->timestamp;
else
@ -335,9 +346,16 @@ class User extends Authenticatable implements MustVerifyEmail,HasLocalePreferenc
public function preferredLocale()
{
\Log::error(print_r($this->company(),1));
$lang = Language::find($this->company()->settings->language_id);
return $lang->locale;
}
public function routeNotificationForMail($notification)
{
return $this->email;
}
}

View File

@ -0,0 +1,117 @@
<?php
namespace App\Notifications;
use App\Mail\RecurringCancellationRequest;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Mail;
class ClientContactRequestCancellation extends Notification implements ShouldQueue
{
use Queueable;
/**
* Create a new notification instance.
*
* @return void
*/
protected $recurring_invoice;
protected $client_contact;
/**
* The callback that should be used to build the mail message.
*
* @var \Closure|null
*/
public static $toMailCallback;
public function __construct($recurring_invoice, $client_contact)
{
$this->recurring_invoice = $recurring_invoice;
$this->client_contact = $client_contact;
}
/**
* Get the notification's delivery channels.
*
* @param mixed $notifiable
* @return array
*/
public function via($notifiable)
{
return ['mail'];
}
/**
* Get the mail representation of the notification.
*
* @param mixed $notifiable
* @return \Illuminate\Notifications\Messages\MailMessage
*/
public function toMail($notifiable)
{
if (static::$toMailCallback) {
return call_user_func(static::$toMailCallback, $notifiable, $this->client_contact);
}
$client_contact_name = $this->client_contact->present()->name();
$client_name = $this->client_contact->client->present()->name();
$recurring_invoice_number = $this->recurring_invoice->invoice_number;
return (new MailMessage)
->subject('Request for recurring invoice cancellation from '.$client_contact_name)
->markdown('email.support.cancellation', [
'message' => "Contact {$client_contact_name} from client {$client_name} requested to cancel Recurring Invoice #{$recurring_invoice_number}",
]);
}
/**
* Get the array representation of the notification.
*
* @param mixed $notifiable
* @return array
*/
public function toArray($notifiable)
{
return [
//
];
}
public function toSlack($notifiable)
{
$name = $this->client_contact->present()->name();
$client_name = $this->client_contact->client->present()->name();
$recurring_invoice_number = $this->recurring_invoice->invoice_number;
return (new SlackMessage)
->success()
->to("#devv2")
->from("System")
->image('https://app.invoiceninja.com/favicon.png')
->content("Contact {$name} from client {$client_name} requested to cancel Recurring Invoice #{$recurring_invoice_number}");
}
/**
* Set a callback that should be used when building the notification mail message.
*
* @param \Closure $callback
* @return void
*/
public static function toMailUsing($callback)
{
static::$toMailCallback = $callback;
}
}

View File

@ -60,11 +60,11 @@ class ClientContactResetPassword extends Notification
}
return (new MailMessage)
->subject(Lang::getFromJson('Reset Password Notification'))
->line(Lang::getFromJson('You are receiving this email because we received a password reset request for your account.'))
->action(Lang::getFromJson('Reset Password'), url(config('app.url').route('client.password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)))
->line(Lang::getFromJson('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.users.expire')]))
->line(Lang::getFromJson('If you did not request a password reset, no further action is required.'));
->subject('Reset Password Notification')
->line('You are receiving this email because we received a password reset request for your account.')
->action('Reset Password', url(config('app.url').route('client.password.reset', ['token' => $this->token, 'email' => $notifiable->getEmailForPasswordReset()], false)))
->line('This password reset link will expire in :count minutes.', ['count' => config('auth.passwords.users.expire')])
->line('If you did not request a password reset, no further action is required.');
}
/**

View File

@ -0,0 +1,30 @@
@component('mail::layout')
{{-- Header --}}
@slot('header')
@component('mail::header', ['url' => config('app.url')])
Header Title
@endcomponent
@endslot
{{-- Body --}}
{{ $message }}
{{-- Subcopy --}}
@isset($subcopy)
@slot('subcopy')
@component('mail::subcopy')
{{ $subcopy }}
@endcomponent
@endslot
@endisset
{{-- Footer --}}
@slot('footer')
@component('mail::footer')
© {{ date('Y') }} {{ config('ninja.app_name') }}.
@endcomponent
@endslot
@endcomponent

View File

@ -82,6 +82,10 @@ Route::group(['middleware' => ['api_db','api_secret_check','token_auth'], 'prefi
Route::resource('tax_rates', 'TaxRateController'); // name = (tasks. index / create / show / update / destroy / edit
Route::post('refresh', 'Auth\LoginController@refresh');
Route::get('templates/{entity}/create', 'TemplateController@create')->name('templates.create');
Route::get('templates/{entity}/{entity_id}', 'TemplateController@show')->name('templates.show');
/*
Route::resource('tasks', 'TaskController'); // name = (tasks. index / create / show / update / destroy / edit