mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-06-23 20:00:33 -04:00
Added preview for email templates
This commit is contained in:
parent
501958cc1e
commit
c79d9fe24c
@ -1301,4 +1301,29 @@ class AccountController extends BaseController
|
||||
|
||||
return Redirect::to("/settings/$section/", 301);
|
||||
}
|
||||
|
||||
public function previewEmail(\App\Services\TemplateService $templateService)
|
||||
{
|
||||
$template = Input::get('template');
|
||||
$invoice = Invoice::scope()->first();
|
||||
$account = Auth::user()->account;
|
||||
|
||||
// replace the variables with sample data
|
||||
$data = [
|
||||
'account' => $account,
|
||||
'invoice' => $invoice,
|
||||
'invitation' => $invoice->invitations->first(),
|
||||
'client' => $invoice->client,
|
||||
'amount' => $invoice->amount
|
||||
];
|
||||
|
||||
// create the email view
|
||||
$view = 'emails.' . $account->getTemplateView() . '_html';
|
||||
$data = array_merge($data, [
|
||||
'body' => $templateService->processVariables($template, $data),
|
||||
'entityType' => ENTITY_INVOICE,
|
||||
]);
|
||||
|
||||
return Response::view($view, $data);
|
||||
}
|
||||
}
|
||||
|
@ -208,6 +208,7 @@ Route::group([
|
||||
Route::resource('tax_rates', 'TaxRateController');
|
||||
Route::post('tax_rates/bulk', 'TaxRateController@bulk');
|
||||
|
||||
Route::get('settings/email_preview', 'AccountController@previewEmail');
|
||||
Route::get('company/{section}/{subSection?}', 'AccountController@redirectLegacy');
|
||||
Route::get('settings/data_visualizations', 'ReportController@d3');
|
||||
Route::get('settings/charts_and_reports', 'ReportController@showReports');
|
||||
|
@ -1169,6 +1169,11 @@ class Account extends Eloquent
|
||||
return str_replace('/>', ' />', $template);
|
||||
}
|
||||
|
||||
public function getTemplateView($view = '')
|
||||
{
|
||||
return $this->getEmailDesignId() == EMAIL_DESIGN_PLAIN ? $view : 'design' . $this->getEmailDesignId();
|
||||
}
|
||||
|
||||
public function getEmailFooter()
|
||||
{
|
||||
if ($this->email_footer) {
|
||||
|
@ -1,17 +1,13 @@
|
||||
<?php namespace App\Ninja\Mailers;
|
||||
|
||||
use Form;
|
||||
use HTML;
|
||||
use Utils;
|
||||
use Event;
|
||||
use URL;
|
||||
use Auth;
|
||||
|
||||
use App\Services\TemplateService;
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Payment;
|
||||
use App\Models\Activity;
|
||||
use App\Models\Gateway;
|
||||
|
||||
use App\Events\InvoiceWasEmailed;
|
||||
use App\Events\QuoteWasEmailed;
|
||||
|
||||
@ -36,6 +32,11 @@ class ContactMailer extends Mailer
|
||||
'paymentButton',
|
||||
];
|
||||
|
||||
public function __construct(TemplateService $templateService)
|
||||
{
|
||||
$this->templateService = $templateService;
|
||||
}
|
||||
|
||||
public function sendInvoice(Invoice $invoice, $reminder = false, $pdfString = false)
|
||||
{
|
||||
$invoice->load('invitations', 'client.language', 'account');
|
||||
@ -144,7 +145,7 @@ class ContactMailer extends Mailer
|
||||
}
|
||||
|
||||
$data = [
|
||||
'body' => $this->processVariables($body, $variables),
|
||||
'body' => $this->templateService->processVariables($body, $variables),
|
||||
'link' => $invitation->getLink(),
|
||||
'entityType' => $invoice->getEntityType(),
|
||||
'invoiceId' => $invoice->id,
|
||||
@ -160,14 +161,9 @@ class ContactMailer extends Mailer
|
||||
$data['pdfFileName'] = $invoice->getFileName();
|
||||
}
|
||||
|
||||
$subject = $this->processVariables($subject, $variables);
|
||||
$subject = $this->templateService->processVariables($subject, $variables);
|
||||
$fromEmail = $user->email;
|
||||
|
||||
if ($account->getEmailDesignId() == EMAIL_DESIGN_PLAIN) {
|
||||
$view = ENTITY_INVOICE;
|
||||
} else {
|
||||
$view = 'design' . ($account->getEmailDesignId() - 1);
|
||||
}
|
||||
$view = $account->getTemplateView(ENTITY_INVOICE);
|
||||
|
||||
$response = $this->sendTo($invitation->contact->email, $fromEmail, $account->getDisplayName(), $subject, $view, $data);
|
||||
|
||||
@ -230,7 +226,7 @@ class ContactMailer extends Mailer
|
||||
];
|
||||
|
||||
$data = [
|
||||
'body' => $this->processVariables($emailTemplate, $variables),
|
||||
'body' => $this->templateService->processVariables($emailTemplate, $variables),
|
||||
'link' => $invitation->getLink(),
|
||||
'invoice' => $invoice,
|
||||
'client' => $client,
|
||||
@ -244,14 +240,10 @@ class ContactMailer extends Mailer
|
||||
$data['pdfFileName'] = $invoice->getFileName();
|
||||
}
|
||||
|
||||
$subject = $this->processVariables($emailSubject, $variables);
|
||||
$subject = $this->templateService->processVariables($emailSubject, $variables);
|
||||
$data['invoice_id'] = $payment->invoice->id;
|
||||
|
||||
if ($account->getEmailDesignId() == EMAIL_DESIGN_PLAIN) {
|
||||
$view = 'payment_confirmation';
|
||||
} else {
|
||||
$view = 'design' . ($account->getEmailDesignId() - 1);
|
||||
}
|
||||
$view = $account->getTemplateView('payment_confirmation');
|
||||
|
||||
if ($user->email && $contact->email) {
|
||||
$this->sendTo($contact->email, $user->email, $accountName, $subject, $view, $data);
|
||||
@ -281,75 +273,4 @@ class ContactMailer extends Mailer
|
||||
|
||||
$this->sendTo($email, CONTACT_EMAIL, CONTACT_NAME, $subject, $view, $data);
|
||||
}
|
||||
|
||||
private function processVariables($template, $data)
|
||||
{
|
||||
$account = $data['account'];
|
||||
$client = $data['client'];
|
||||
$invitation = $data['invitation'];
|
||||
$invoice = $invitation->invoice;
|
||||
$passwordHTML = isset($data['password'])?'<p>'.trans('texts.password').': '.$data['password'].'<p>':false;
|
||||
$documentsHTML = '';
|
||||
|
||||
if($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()){
|
||||
$documentsHTML .= trans('texts.email_documents_header').'<ul>';
|
||||
foreach($invoice->documents as $document){
|
||||
$documentsHTML .= '<li><a href="'.HTML::entities($document->getClientUrl($invitation)).'">'.HTML::entities($document->name).'</a></li>';
|
||||
}
|
||||
foreach($invoice->expenses as $expense){
|
||||
foreach($expense->documents as $document){
|
||||
$documentsHTML .= '<li><a href="'.HTML::entities($document->getClientUrl($invitation)).'">'.HTML::entities($document->name).'</a></li>';
|
||||
}
|
||||
}
|
||||
$documentsHTML .= '</ul>';
|
||||
}
|
||||
|
||||
$variables = [
|
||||
'$footer' => $account->getEmailFooter(),
|
||||
'$client' => $client->getDisplayName(),
|
||||
'$account' => $account->getDisplayName(),
|
||||
'$dueDate' => $account->formatDate($invoice->due_date),
|
||||
'$invoiceDate' => $account->formatDate($invoice->invoice_date),
|
||||
'$contact' => $invitation->contact->getDisplayName(),
|
||||
'$firstName' => $invitation->contact->first_name,
|
||||
'$amount' => $account->formatMoney($data['amount'], $client),
|
||||
'$invoice' => $invoice->invoice_number,
|
||||
'$quote' => $invoice->invoice_number,
|
||||
'$link' => $invitation->getLink(),
|
||||
'$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,
|
||||
'$customInvoice2' => $account->custom_invoice_text_label2,
|
||||
'$documents' => $documentsHTML,
|
||||
];
|
||||
|
||||
// Add variables for available payment types
|
||||
foreach (Gateway::$paymentTypes as $type) {
|
||||
$camelType = Gateway::getPaymentTypeName($type);
|
||||
$type = Utils::toSnakeCase($camelType);
|
||||
$variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}";
|
||||
$variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}");
|
||||
}
|
||||
|
||||
$includesPasswordPlaceholder = strpos($template, '$password') !== false;
|
||||
|
||||
$str = str_replace(array_keys($variables), array_values($variables), $template);
|
||||
|
||||
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);
|
||||
$str = autolink($str, 100);
|
||||
|
||||
return $str;
|
||||
}
|
||||
}
|
||||
|
80
app/Services/TemplateService.php
Normal file
80
app/Services/TemplateService.php
Normal file
@ -0,0 +1,80 @@
|
||||
<?php namespace App\Services;
|
||||
|
||||
use Form;
|
||||
use HTML;
|
||||
use Utils;
|
||||
use App\Models\Gateway;
|
||||
|
||||
class TemplateService
|
||||
{
|
||||
public function processVariables($template, $data)
|
||||
{
|
||||
$account = $data['account'];
|
||||
$client = $data['client'];
|
||||
$invitation = $data['invitation'];
|
||||
$invoice = $invitation->invoice;
|
||||
$passwordHTML = isset($data['password'])?'<p>'.trans('texts.password').': '.$data['password'].'<p>':false;
|
||||
$documentsHTML = '';
|
||||
|
||||
if ($account->hasFeature(FEATURE_DOCUMENTS) && $invoice->hasDocuments()) {
|
||||
$documentsHTML .= trans('texts.email_documents_header').'<ul>';
|
||||
foreach($invoice->documents as $document){
|
||||
$documentsHTML .= '<li><a href="'.HTML::entities($document->getClientUrl($invitation)).'">'.HTML::entities($document->name).'</a></li>';
|
||||
}
|
||||
foreach($invoice->expenses as $expense){
|
||||
foreach($expense->documents as $document){
|
||||
$documentsHTML .= '<li><a href="'.HTML::entities($document->getClientUrl($invitation)).'">'.HTML::entities($document->name).'</a></li>';
|
||||
}
|
||||
}
|
||||
$documentsHTML .= '</ul>';
|
||||
}
|
||||
|
||||
$variables = [
|
||||
'$footer' => $account->getEmailFooter(),
|
||||
'$client' => $client->getDisplayName(),
|
||||
'$account' => $account->getDisplayName(),
|
||||
'$dueDate' => $account->formatDate($invoice->due_date),
|
||||
'$invoiceDate' => $account->formatDate($invoice->invoice_date),
|
||||
'$contact' => $invitation->contact->getDisplayName(),
|
||||
'$firstName' => $invitation->contact->first_name,
|
||||
'$amount' => $account->formatMoney($data['amount'], $client),
|
||||
'$invoice' => $invoice->invoice_number,
|
||||
'$quote' => $invoice->invoice_number,
|
||||
'$link' => $invitation->getLink(),
|
||||
'$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,
|
||||
'$customInvoice2' => $account->custom_invoice_text_label2,
|
||||
'$documents' => $documentsHTML,
|
||||
];
|
||||
|
||||
// Add variables for available payment types
|
||||
foreach (Gateway::$paymentTypes as $type) {
|
||||
$camelType = Gateway::getPaymentTypeName($type);
|
||||
$type = Utils::toSnakeCase($camelType);
|
||||
$variables["\${$camelType}Link"] = $invitation->getLink('payment') . "/{$type}";
|
||||
$variables["\${$camelType}Button"] = Form::emailPaymentButton($invitation->getLink('payment') . "/{$type}");
|
||||
}
|
||||
|
||||
$includesPasswordPlaceholder = strpos($template, '$password') !== false;
|
||||
|
||||
$str = str_replace(array_keys($variables), array_values($variables), $template);
|
||||
|
||||
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);
|
||||
$str = autolink($str, 100);
|
||||
|
||||
return $str;
|
||||
}
|
||||
}
|
@ -1176,6 +1176,7 @@ $LANG = array(
|
||||
'page_size' => 'Page Size',
|
||||
'live_preview_disabled' => 'Live preview has been disabled to support selected font',
|
||||
'invoice_number_padding' => 'Padding',
|
||||
'preview' => 'Preview',
|
||||
|
||||
);
|
||||
|
||||
|
@ -62,11 +62,14 @@
|
||||
<div id="{{ $field }}_template_preview"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<p> <p/>
|
||||
<div class="row">
|
||||
<div class="col-md-10">
|
||||
@include('partials/quill_toolbar', ['name' => $field])
|
||||
</div>
|
||||
<div class="col-md-2" style="padding-top:10px">
|
||||
{!! Button::primary(trans('texts.preview'))->withAttributes(['onclick' => 'serverPreview("'.$field.'")'])->small() !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -80,6 +80,26 @@
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal fade" id="templatePreviewModal" tabindex="-1" role="dialog" aria-labelledby="templatePreviewModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" style="min-width:700px">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h4 class="modal-title" id="templatePreviewModalLabel">{{ trans('texts.preview') }}</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<iframe id="server-preview" frameborder="1" width="100%" height="500px"/></iframe>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer" style="margin-top: 0px">
|
||||
<button type="button" class="btn btn-primary" data-dismiss="modal">{{ trans('texts.close') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="modal fade" id="templateHelpModal" tabindex="-1" role="dialog" aria-labelledby="templateHelpModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" style="min-width:150px">
|
||||
<div class="modal-content">
|
||||
@ -158,6 +178,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
function serverPreview(field) {
|
||||
console.log(field);
|
||||
$('#templatePreviewModal').modal('show');
|
||||
var template = $('#email_template_' + field).val();
|
||||
var url = '{{ URL::to('settings/email_preview') }}?template=' + template;
|
||||
$('#server-preview').attr('src', url).load(function() {
|
||||
// disable links in the preview
|
||||
$('iframe').contents().find('a').each(function(index) {
|
||||
$(this).on('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
$(function() {
|
||||
for (var i=0; i<entityTypes.length; i++) {
|
||||
var entityType = entityTypes[i];
|
||||
|
@ -12,30 +12,30 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border-collapse: collapse;">
|
||||
<table cellpadding="10" cellspacing="0" border="0" bgcolor="{{ $account->primary_color ?: '#2E2B2B' }}" width="580" align="center" class="header"
|
||||
style="border-bottom-width: 6px; border-bottom-color: {{ $account->primary_color ?: '#2E2B2B' }}; border-bottom-style: solid;">
|
||||
<table cellpadding="10" cellspacing="0" border="0" bgcolor="#F4F5F5" width="580" align="center"
|
||||
class="header" style="border-top-width: 6px; border-top-color: {{ $account->primary_color ?: '#2E2B2B' }}; border-top-style: solid;">
|
||||
<tr>
|
||||
<td class="logo" width="205" style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
<td class="logo" width="208" style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
@include('emails.partials.account_logo')
|
||||
</td>
|
||||
<td width="183" style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
<p class="left" style="line-height: 22px; margin: 3px 0 0; padding: 0;">
|
||||
<td width="183" style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
<p class="left" style="line-height: 22px; margin: 0; padding: 2px 0 0;">
|
||||
@if ($invoice->due_date)
|
||||
<span style="font-size: 11px; color: #8f8d8e;">
|
||||
{{ strtoupper(trans('texts.due_by', ['date' => $account->formatDate($invoice->due_date)])) }}
|
||||
</span><br />
|
||||
@endif
|
||||
<span style="font-size: 19px; color: #FFFFFF;">
|
||||
<span style="font-size: 18px;">
|
||||
{{ trans("texts.{$entityType}") }} {{ $invoice->invoice_number }}
|
||||
</span>
|
||||
</p>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
<p style="margin: 0; padding: 0;">
|
||||
<span style="font-size: 12px; color: #8f8d8e;">
|
||||
{{ strtoupper(trans('texts.' . $invoice->present()->balanceDueLabel)) }}:
|
||||
<td style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
<p class="right" style="line-height: 14px; margin: 0; padding: 0;">
|
||||
<span style="font-size: 15px; color: #231F20;">
|
||||
{{ trans('texts.' . $invoice->present()->balanceDueLabel) }}:
|
||||
</span><br />
|
||||
<span class="total" style="font-size: 27px; color: #FFFFFF; margin-top: 5px;display: block;">
|
||||
<span class="total" style="font-size: 26px; display: block;margin-top: 5px;">
|
||||
{{ $account->formatMoney($invoice->getRequestedAmount(), $client) }}
|
||||
</span>
|
||||
</p>
|
||||
|
@ -12,30 +12,30 @@
|
||||
</tr>
|
||||
<tr>
|
||||
<td style="border-collapse: collapse;">
|
||||
<table cellpadding="10" cellspacing="0" border="0" bgcolor="#F4F5F5" width="580" align="center"
|
||||
class="header" style="border-top-width: 6px; border-top-color: {{ $account->primary_color ?: '#2E2B2B' }}; border-top-style: solid;">
|
||||
<table cellpadding="10" cellspacing="0" border="0" bgcolor="{{ $account->primary_color ?: '#2E2B2B' }}" width="580" align="center" class="header"
|
||||
style="border-bottom-width: 6px; border-bottom-color: {{ $account->primary_color ?: '#2E2B2B' }}; border-bottom-style: solid;">
|
||||
<tr>
|
||||
<td class="logo" width="208" style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
<td class="logo" width="205" style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
@include('emails.partials.account_logo')
|
||||
</td>
|
||||
<td width="183" style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
<p class="left" style="line-height: 22px; margin: 0; padding: 2px 0 0;">
|
||||
<td width="183" style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
<p class="left" style="line-height: 22px; margin: 3px 0 0; padding: 0;">
|
||||
@if ($invoice->due_date)
|
||||
<span style="font-size: 11px; color: #8f8d8e;">
|
||||
{{ strtoupper(trans('texts.due_by', ['date' => $account->formatDate($invoice->due_date)])) }}
|
||||
</span><br />
|
||||
@endif
|
||||
<span style="font-size: 18px;">
|
||||
<span style="font-size: 19px; color: #FFFFFF;">
|
||||
{{ trans("texts.{$entityType}") }} {{ $invoice->invoice_number }}
|
||||
</span>
|
||||
</p>
|
||||
</td>
|
||||
<td style="border-collapse: collapse; vertical-align: middle;" valign="middle">
|
||||
<p class="right" style="line-height: 14px; margin: 0; padding: 0;">
|
||||
<span style="font-size: 15px; color: #231F20;">
|
||||
{{ trans('texts.' . $invoice->present()->balanceDueLabel) }}:
|
||||
<td style="border-collapse: collapse; vertical-align: middle; line-height: 16px;" valign="middle">
|
||||
<p style="margin: 0; padding: 0;">
|
||||
<span style="font-size: 12px; color: #8f8d8e;">
|
||||
{{ strtoupper(trans('texts.' . $invoice->present()->balanceDueLabel)) }}:
|
||||
</span><br />
|
||||
<span class="total" style="font-size: 26px; display: block;margin-top: 5px;">
|
||||
<span class="total" style="font-size: 27px; color: #FFFFFF; margin-top: 5px;display: block;">
|
||||
{{ $account->formatMoney($invoice->getRequestedAmount(), $client) }}
|
||||
</span>
|
||||
</p>
|
@ -3,7 +3,7 @@
|
||||
<a href="{{ $account->website }}" style="color: #19BB40; text-decoration: underline;">
|
||||
@endif
|
||||
|
||||
<img src="{{ $message->embed($account->getLogoURL()) }}" style="max-height:50px; max-width:140px; margin-left: 33px;" />
|
||||
<img src="{{ isset($message) ? $message->embed($account->getLogoURL()) : $account->getLogoURL() }}" style="max-height:50px; max-width:140px; margin-left: 33px;" />
|
||||
|
||||
@if ($account->website)
|
||||
</a>
|
||||
|
Loading…
x
Reference in New Issue
Block a user