diff --git a/Gruntfile.js b/Gruntfile.js index 0b004f4ba583..ce22ba6de51a 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -171,7 +171,7 @@ module.exports = function(grunt) { 'public/js/pdf_viewer.js', 'public/js/compatibility.js', 'public/js/pdfmake.min.js', - 'public/js/vfs_fonts.js', + 'public/js/vfs.js', ], dest: 'public/pdf.built.js', nonull: true diff --git a/app/Http/Controllers/AccountController.php b/app/Http/Controllers/AccountController.php index 912ade56f962..6d06a2611022 100644 --- a/app/Http/Controllers/AccountController.php +++ b/app/Http/Controllers/AccountController.php @@ -347,6 +347,7 @@ class AccountController extends BaseController $client = new stdClass(); $contact = new stdClass(); $invoiceItem = new stdClass(); + $document = new stdClass(); $client->name = 'Sample Client'; $client->address1 = trans('texts.address1'); @@ -372,8 +373,11 @@ class AccountController extends BaseController $invoiceItem->notes = 'Notes'; $invoiceItem->product_key = 'Item'; + $document->base64 = 'data:image/jpeg;base64,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'; + $invoice->client = $client; $invoice->invoice_items = [$invoiceItem]; + $invoice->documents = $account->isPro()?[$document,$document,$document,$document]:[]; $data['account'] = $account; $data['invoice'] = $invoice; @@ -749,6 +753,7 @@ class AccountController extends BaseController $account->hide_paid_to_date = Input::get('hide_paid_to_date') ? true : false; $account->all_pages_header = Input::get('all_pages_header') ? true : false; $account->all_pages_footer = Input::get('all_pages_footer') ? true : false; + $account->invoice_embed_documents = Input::get('invoice_embed_documents') ? true : false; $account->header_font_id = Input::get('header_font_id'); $account->body_font_id = Input::get('body_font_id'); $account->primary_color = Input::get('primary_color'); diff --git a/app/Http/Controllers/DocumentController.php b/app/Http/Controllers/DocumentController.php index 4b7718bfd817..d19909bae0ad 100644 --- a/app/Http/Controllers/DocumentController.php +++ b/app/Http/Controllers/DocumentController.php @@ -70,6 +70,31 @@ class DocumentController extends BaseController return $response; } + public function getVFSJS($publicId, $name){ + $document = Document::scope($publicId) + ->firstOrFail(); + + if(substr($name, -3)=='.js'){ + $name = substr($name, 0, -3); + } + + if(!$this->checkViewPermission($document, $response)){ + return $response; + } + + if(substr($document->type, 0, 6) != 'image/'){ + return Response::view('error', array('error'=>'Image does not exist!'), 404); + } + + $content = $document->preview?$document->getRawPreview():$document->getRaw(); + $content = 'ninjaAddVFSDoc('.json_encode(intval($publicId).'/'.strval($name)).',"'.base64_encode($content).'")'; + $response = Response::make($content, 200); + $response->header('content-type', 'text/javascript'); + $response->header('cache-control', 'max-age=31536000'); + + return $response; + } + public function postUpload() { if (!Utils::isPro()) { diff --git a/app/Http/Controllers/InvoiceController.php b/app/Http/Controllers/InvoiceController.php index 138909a9e13d..8cd6049d0be4 100644 --- a/app/Http/Controllers/InvoiceController.php +++ b/app/Http/Controllers/InvoiceController.php @@ -536,7 +536,7 @@ class InvoiceController extends BaseController public function invoiceHistory($publicId) { $invoice = Invoice::withTrashed()->scope($publicId)->firstOrFail(); - $invoice->load('user', 'invoice_items', 'account.country', 'client.contacts', 'client.country'); + $invoice->load('user', 'invoice_items', 'documents', 'account.country', 'client.contacts', 'client.country'); $invoice->invoice_date = Utils::fromSqlDate($invoice->invoice_date); $invoice->due_date = Utils::fromSqlDate($invoice->due_date); $invoice->is_pro = Auth::user()->isPro(); diff --git a/app/Http/Controllers/PublicClientController.php b/app/Http/Controllers/PublicClientController.php index d067ff38a740..c854189ac240 100644 --- a/app/Http/Controllers/PublicClientController.php +++ b/app/Http/Controllers/PublicClientController.php @@ -7,10 +7,12 @@ use URL; use Input; use Utils; use Request; +use Response; use Session; use Datatable; use App\Models\Gateway; use App\Models\Invitation; +use App\Models\Document; use App\Ninja\Repositories\InvoiceRepository; use App\Ninja\Repositories\PaymentRepository; use App\Ninja\Repositories\ActivityRepository; @@ -22,8 +24,9 @@ class PublicClientController extends BaseController { private $invoiceRepo; private $paymentRepo; + private $documentRepo; - public function __construct(InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo, ActivityRepository $activityRepo, PaymentService $paymentService) + public function __construct(InvoiceRepository $invoiceRepo, PaymentRepository $paymentRepo, ActivityRepository $activityRepo, PaymentService $paymentService) { $this->invoiceRepo = $invoiceRepo; $this->paymentRepo = $paymentRepo; @@ -372,5 +375,44 @@ class PublicClientController extends BaseController return $invitation; } + + + + public function getDocumentVFSJS($publicId, $name){ + if (!$invitation = $this->getInvitation()) { + return $this->returnError(); + } + + $clientId = $invitation->invoice->client_id; + $document = Document::scope($publicId, $invitation->account_id)->first(); + + + if(!$document || substr($document->type, 0, 6) != 'image/'){ + return Response::view('error', array('error'=>'Image does not exist!'), 404); + } + + $authorized = false; + if($document->expense && $document->expense->client_id == $invitation->invoice->client_id){ + $authorized = true; + } else if($document->invoice && $document->invoice->client_id == $invitation->invoice->client_id){ + $authorized = true; + } + + if(!$authorized){ + return Response::view('error', array('error'=>'Not authorized'), 403); + } + + if(substr($name, -3)=='.js'){ + $name = substr($name, 0, -3); + } + + $content = $document->preview?$document->getRawPreview():$document->getRaw(); + $content = 'ninjaAddVFSDoc('.json_encode(intval($publicId).'/'.strval($name)).',"'.base64_encode($content).'")'; + $response = Response::make($content, 200); + $response->header('content-type', 'text/javascript'); + $response->header('cache-control', 'max-age=31536000'); + + return $response; + } } diff --git a/app/Http/routes.php b/app/Http/routes.php index 6a4970194ca6..e70444847a58 100644 --- a/app/Http/routes.php +++ b/app/Http/routes.php @@ -47,6 +47,7 @@ Route::group(['middleware' => 'auth:client'], function() { Route::get('client/invoices', 'PublicClientController@invoiceIndex'); Route::get('client/payments', 'PublicClientController@paymentIndex'); Route::get('client/dashboard', 'PublicClientController@dashboard'); + Route::get('client/document/js/{public_id}/{filename}', 'PublicClientController@getDocumentVFSJS'); }); Route::get('api/client.quotes', array('as'=>'api.client.quotes', 'uses'=>'PublicClientController@quoteDatatable')); @@ -133,6 +134,7 @@ Route::group(['middleware' => 'auth:user'], function() { Route::post('recurring_invoices/bulk', 'InvoiceController@bulk'); Route::get('document/{public_id}/{filename?}', 'DocumentController@get'); + Route::get('document/js/{public_id}/{filename}', 'DocumentController@getVFSJS'); Route::get('document/preview/{public_id}/{filename?}', 'DocumentController@getPreview'); Route::post('document', 'DocumentController@postUpload'); @@ -430,7 +432,7 @@ if (!defined('CONTACT_EMAIL')) { define('MAX_LOGO_FILE_SIZE', 200); // KB define('MAX_FAILED_LOGINS', 10); define('MAX_DOCUMENT_SIZE', env('MAX_DOCUMENT_SIZE', 10000));// KB - define('DOCUMENT_PREVIEW_SIZE', env('DOCUMENT_PREVIEW_SIZE', 150));// pixels + define('DOCUMENT_PREVIEW_SIZE', env('DOCUMENT_PREVIEW_SIZE', 300));// pixels define('DEFAULT_FONT_SIZE', 9); define('DEFAULT_HEADER_FONT', 1);// Roboto define('DEFAULT_BODY_FONT', 1);// Roboto diff --git a/app/Models/Document.php b/app/Models/Document.php index f342d1ff08e1..2d61cb7e3ba6 100644 --- a/app/Models/Document.php +++ b/app/Models/Document.php @@ -132,6 +132,14 @@ class Document extends EntityModel return url('document/'.$this->public_id.'/'.$this->name); } + public function getVFSJSUrl(){ + return url('document/js/'.$this->public_id.'/'.$this->name.'.js'); + } + + public function getClientVFSJSUrl(){ + return url('client/document/js/'.$this->public_id.'/'.$this->name.'.js'); + } + public function getPreviewUrl(){ return $this->preview?url('document/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null; } diff --git a/app/Ninja/Repositories/DocumentRepository.php b/app/Ninja/Repositories/DocumentRepository.php index 8697a232624e..08af5bb57780 100644 --- a/app/Ninja/Repositories/DocumentRepository.php +++ b/app/Ninja/Repositories/DocumentRepository.php @@ -94,12 +94,14 @@ class DocumentRepository extends BaseRepository if(in_array($documentType, array('image/jpeg','image/png','image/gif','image/bmp','image/tiff'))){ $makePreview = false; $imageSize = getimagesize($filePath); + $width = $imageSize[0]; + $height = $imageSize[1]; $imgManagerConfig = array(); if(in_array($documentType, array('image/gif','image/bmp','image/tiff'))){ // Needs to be converted $makePreview = true; } else { - if($imageSize[0] > DOCUMENT_PREVIEW_SIZE || $imageSize[1] > DOCUMENT_PREVIEW_SIZE){ + if($width > DOCUMENT_PREVIEW_SIZE || $height > DOCUMENT_PREVIEW_SIZE){ $makePreview = true; } } @@ -126,14 +128,30 @@ class DocumentRepository extends BaseRepository $imgManager = new ImageManager($imgManagerConfig); $img = $imgManager->make($filePath); - $img->fit(DOCUMENT_PREVIEW_SIZE, DOCUMENT_PREVIEW_SIZE, function ($constraint) { - $constraint->upsize(); - }); + + if($width <= DOCUMENT_PREVIEW_SIZE && $height <= DOCUMENT_PREVIEW_SIZE){ + $previewWidth = $width; + $previewHeight = $height; + } else if($width > $height) { + $previewWidth = DOCUMENT_PREVIEW_SIZE; + $previewHeight = $height * DOCUMENT_PREVIEW_SIZE / $width; + } else { + $previewHeight = DOCUMENT_PREVIEW_SIZE; + $previewWidth = $width * DOCUMENT_PREVIEW_SIZE / $height; + } + + $img->resize($previewWidth, $previewHeight); $previewContent = (string) $img->encode($previewType); $disk->put($document->preview, $previewContent); + $base64 = base64_encode($previewContent); } - } + else{ + $base64 = base64_encode($disk->get($document->preview)); + } + }else{ + $base64 = base64_encode(file_get_contents($filePath)); + } } $document->path = $filename; @@ -147,11 +165,16 @@ class DocumentRepository extends BaseRepository } $document->save(); + $doc_array = $document->toArray(); + if(!empty($base64)){ + $mime = !empty($previewType)?Document::$extensions[$previewType]:$documentType; + $doc_array['base64'] = 'data:'.$mime.';base64,'.$base64; + } return Response::json([ 'error' => false, - 'document' => $document, + 'document' => $doc_array, 'code' => 200 ], 200); } diff --git a/app/Ninja/Repositories/InvoiceRepository.php b/app/Ninja/Repositories/InvoiceRepository.php index d69df1e2ae54..bf9cc59c39df 100644 --- a/app/Ninja/Repositories/InvoiceRepository.php +++ b/app/Ninja/Repositories/InvoiceRepository.php @@ -600,7 +600,7 @@ class InvoiceRepository extends BaseRepository return false; } - $invoice->load('user', 'invoice_items', 'invoice_design', 'account.country', 'client.contacts', 'client.country'); + $invoice->load('user', 'invoice_items', 'documents', 'invoice_design', 'account.country', 'client.contacts', 'client.country'); $client = $invoice->client; if (!$client || $client->is_deleted) { diff --git a/database/migrations/2016_03_22_168362_add_documents.php b/database/migrations/2016_03_22_168362_add_documents.php index f37ab0e859d5..1fcaea379021 100644 --- a/database/migrations/2016_03_22_168362_add_documents.php +++ b/database/migrations/2016_03_22_168362_add_documents.php @@ -10,14 +10,15 @@ class AddDocuments extends Migration { */ public function up() { - /*Schema::table('accounts', function($table) { + Schema::table('accounts', function($table) { $table->string('logo')->nullable()->default(null); $table->unsignedInteger('logo_width'); $table->unsignedInteger('logo_height'); $table->unsignedInteger('logo_size'); + $table->boolean('invoice_embed_documents')->default(1); }); - DB::table('accounts')->update(array('logo' => ''));*/ + DB::table('accounts')->update(array('logo' => '')); Schema::dropIfExists('documents'); Schema::create('documents', function($t) { @@ -59,6 +60,7 @@ class AddDocuments extends Migration { $table->dropColumn('logo_width'); $table->dropColumn('logo_height'); $table->dropColumn('logo_size'); + $table->dropColumn('invoice_embed_documents'); }); Schema::dropIfExists('documents'); diff --git a/public/built.js b/public/built.js index 8b12b9693fbb..6a2954d3a786 100644 --- a/public/built.js +++ b/public/built.js @@ -31101,11 +31101,12 @@ function GetPdfMake(invoice, javascript, callback) { function addFont(font){ if(window.ninjaFontVfs[font.folder]){ + folder = 'fonts/'+font.folder; pdfMake.fonts[font.name] = { - normal: font.folder+'/'+font.normal, - italics: font.folder+'/'+font.italics, - bold: font.folder+'/'+font.bold, - bolditalics: font.folder+'/'+font.bolditalics + normal: folder+'/'+font.normal, + italics: folder+'/'+font.italics, + bold: folder+'/'+font.bold, + bolditalics: folder+'/'+font.bolditalics } } } @@ -31136,6 +31137,7 @@ NINJA.decodeJavascript = function(invoice, javascript) 'invoiceDetailsHeight': (NINJA.invoiceDetails(invoice).length * 16) + 16, 'invoiceLineItems': NINJA.invoiceLines(invoice), 'invoiceLineItemColumns': NINJA.invoiceColumns(invoice), + 'invoiceDocuments' : NINJA.invoiceDocuments(invoice), 'quantityWidth': NINJA.quantityWidth(invoice), 'taxWidth': NINJA.taxWidth(invoice), 'clientDetails': NINJA.clientDetails(invoice), @@ -31393,6 +31395,31 @@ NINJA.invoiceLines = function(invoice) { return NINJA.prepareDataTable(grid, 'invoiceItems'); } +NINJA.invoiceDocuments = function(invoice) { + if(!invoice.documents || !invoice.account.invoice_embed_documents)return[]; + var stack = []; + var stackItem = null; + + var j = 0; + for (var i = 0; i < invoice.documents.length; i++) { + var document = invoice.documents[i]; + var path = document.base64; + if(!path && (document.preview_url || document.type == 'image/png' || document.type == 'image/jpeg')){ + path = 'docs/'+document.public_id+'/'+document.name; + } + if(path && (window.pdfMake.vfs[path] || document.base64)){ + if(j%3==0){ + stackItem = {columns:[]}; + stack.push(stackItem); + } + stackItem.columns.push({stack:[{image:path,style:'invoiceDocument',fit:[150,150]}], width:175}) + j++; + } + } + + return {stack:stack}; +} + NINJA.subtotals = function(invoice, hideBalance) { if (!invoice) { diff --git a/public/js/pdf.pdfmake.js b/public/js/pdf.pdfmake.js index f8f30f4b546f..6989916a887e 100644 --- a/public/js/pdf.pdfmake.js +++ b/public/js/pdf.pdfmake.js @@ -109,11 +109,12 @@ function GetPdfMake(invoice, javascript, callback) { function addFont(font){ if(window.ninjaFontVfs[font.folder]){ + folder = 'fonts/'+font.folder; pdfMake.fonts[font.name] = { - normal: font.folder+'/'+font.normal, - italics: font.folder+'/'+font.italics, - bold: font.folder+'/'+font.bold, - bolditalics: font.folder+'/'+font.bolditalics + normal: folder+'/'+font.normal, + italics: folder+'/'+font.italics, + bold: folder+'/'+font.bold, + bolditalics: folder+'/'+font.bolditalics } } } @@ -144,6 +145,7 @@ NINJA.decodeJavascript = function(invoice, javascript) 'invoiceDetailsHeight': (NINJA.invoiceDetails(invoice).length * 16) + 16, 'invoiceLineItems': NINJA.invoiceLines(invoice), 'invoiceLineItemColumns': NINJA.invoiceColumns(invoice), + 'invoiceDocuments' : NINJA.invoiceDocuments(invoice), 'quantityWidth': NINJA.quantityWidth(invoice), 'taxWidth': NINJA.taxWidth(invoice), 'clientDetails': NINJA.clientDetails(invoice), @@ -401,6 +403,31 @@ NINJA.invoiceLines = function(invoice) { return NINJA.prepareDataTable(grid, 'invoiceItems'); } +NINJA.invoiceDocuments = function(invoice) { + if(!invoice.documents || !invoice.account.invoice_embed_documents)return[]; + var stack = []; + var stackItem = null; + + var j = 0; + for (var i = 0; i < invoice.documents.length; i++) { + var document = invoice.documents[i]; + var path = document.base64; + if(!path && (document.preview_url || document.type == 'image/png' || document.type == 'image/jpeg')){ + path = 'docs/'+document.public_id+'/'+document.name; + } + if(path && (window.pdfMake.vfs[path] || document.base64)){ + if(j%3==0){ + stackItem = {columns:[]}; + stack.push(stackItem); + } + stackItem.columns.push({stack:[{image:path,style:'invoiceDocument',fit:[150,150]}], width:175}) + j++; + } + } + + return {stack:stack}; +} + NINJA.subtotals = function(invoice, hideBalance) { if (!invoice) { diff --git a/public/js/vfs_fonts.js b/public/js/vfs.js similarity index 52% rename from public/js/vfs_fonts.js rename to public/js/vfs.js index 76e2d87ee432..fcdb3d206138 100644 --- a/public/js/vfs_fonts.js +++ b/public/js/vfs.js @@ -3,7 +3,12 @@ if(window.ninjaFontVfs)ninjaLoadFontVfs(); function ninjaLoadFontVfs(){ jQuery.each(window.ninjaFontVfs, function(font, files){ jQuery.each(files, function(filename, file){ - window.pdfMake.vfs[font+'/'+filename] = file; + window.pdfMake.vfs['fonts/'+font+'/'+filename] = file; }); }) +} +function ninjaAddVFSDoc(name,content){ + window.pdfMake.vfs['docs/'+name] = content; + if(window.refreshPDF)refreshPDF(true); + jQuery(document).trigger('ninjaVFSDocAdded'); } \ No newline at end of file diff --git a/public/pdf.built.js b/public/pdf.built.js index 9b663502b712..892149f88bd9 100644 --- a/public/pdf.built.js +++ b/public/pdf.built.js @@ -7926,7 +7926,12 @@ if(window.ninjaFontVfs)ninjaLoadFontVfs(); function ninjaLoadFontVfs(){ jQuery.each(window.ninjaFontVfs, function(font, files){ jQuery.each(files, function(filename, file){ - window.pdfMake.vfs[font+'/'+filename] = file; + window.pdfMake.vfs['fonts/'+font+'/'+filename] = file; }); }) +} +function ninjaAddVFSDoc(name,content){ + window.pdfMake.vfs['docs/'+name] = content; + if(window.refreshPDF)refreshPDF(true); + jQuery(document).trigger('ninjaVFSDocAdded'); } \ No newline at end of file diff --git a/resources/lang/en/texts.php b/resources/lang/en/texts.php index b855b65c9a6c..284ad739d853 100644 --- a/resources/lang/en/texts.php +++ b/resources/lang/en/texts.php @@ -1099,8 +1099,9 @@ $LANG = array( // Documents 'invoice_documents' => 'Attached Documents', - 'document_upload_message' => 'Drop files here or click to upload.' - + 'document_upload_message' => 'Drop files here or click to upload.', + 'invoice_embed_documents' => 'Embed Documents', + 'invoice_embed_documents_help' => 'Include attached images in the invoice.', ); return $LANG; diff --git a/resources/views/accounts/invoice_design.blade.php b/resources/views/accounts/invoice_design.blade.php index 4af173818dad..9576afbf1d92 100644 --- a/resources/views/accounts/invoice_design.blade.php +++ b/resources/views/accounts/invoice_design.blade.php @@ -48,6 +48,7 @@ function getPDFString(cb) { invoice.is_pro = {!! Auth::user()->isPro() ? 'true' : 'false' !!}; invoice.account.hide_quantity = $('#hide_quantity').is(":checked"); + invoice.account.invoice_embed_documents = $('#invoice_embed_documents').is(":checked"); invoice.account.hide_paid_to_date = $('#hide_paid_to_date').is(":checked"); invoice.invoice_design_id = $('#invoice_design_id').val(); @@ -207,6 +208,7 @@ {!! Former::checkbox('hide_quantity')->text(trans('texts.hide_quantity_help')) !!} {!! Former::checkbox('hide_paid_to_date')->text(trans('texts.hide_paid_to_date_help')) !!} + {!! Former::checkbox('invoice_embed_documents')->text(trans('texts.invoice_embed_documents_help')) !!} diff --git a/resources/views/invoices/edit.blade.php b/resources/views/invoices/edit.blade.php index 0a9810d4160f..bf2ce391c67a 100644 --- a/resources/views/invoices/edit.blade.php +++ b/resources/views/invoices/edit.blade.php @@ -270,7 +270,7 @@
  • {{ trans("texts.{$entityType}_terms") }}
  • {{ trans("texts.{$entityType}_footer") }}
  • - @if (Auth::user()->account->isPro()) + @if ($account->isPro())
  • {{ trans("texts.{$entityType}_documents") }}
  • @endif @@ -304,7 +304,7 @@ ') !!} - @if (Auth::user()->account->isPro()) + @if ($account->isPro())
    @@ -1319,7 +1319,7 @@ model.invoice().invoice_number(number); } - @if (Auth::user()->account->isPro()) + @if ($account->isPro()) function handleDocumentAdded(file){ if(file.mock)return; file.index = model.invoice().documents().length; @@ -1328,14 +1328,21 @@ function handleDocumentRemoved(file){ model.invoice().removeDocument(file.public_id); + refreshPDF(true); } function handleDocumentUploaded(file, response){ file.public_id = response.document.public_id model.invoice().documents()[file.index].update(response.document); + refreshPDF(true); } @endif + @if ($account->isPro() && $account->invoice_embed_documents) + @foreach ($invoice->documents as $document) + + @endforeach + @endif @stop diff --git a/resources/views/invoices/history.blade.php b/resources/views/invoices/history.blade.php index 3a9fd36e333d..19d7eeff5223 100644 --- a/resources/views/invoices/history.blade.php +++ b/resources/views/invoices/history.blade.php @@ -56,5 +56,10 @@
     
    @include('invoices.pdf', ['account' => Auth::user()->account, 'pdfHeight' => 800]) - + + @if (Auth::user()->account->isPro() && Auth::user()->account->invoice_embed_documents) + @foreach ($invoice->documents as $document) + + @endforeach + @endif @stop \ No newline at end of file diff --git a/resources/views/invoices/pdf.blade.php b/resources/views/invoices/pdf.blade.php index 2c4d97cf97ed..1c8389affeba 100644 --- a/resources/views/invoices/pdf.blade.php +++ b/resources/views/invoices/pdf.blade.php @@ -123,7 +123,7 @@ $('#theFrame').attr('src', string).show(); } else { if (isRefreshing) { - //needsRefresh = true; + needsRefresh = true; return; } isRefreshing = true; diff --git a/resources/views/invoices/view.blade.php b/resources/views/invoices/view.blade.php index c1ba3822899a..2ad564f9fdd7 100644 --- a/resources/views/invoices/view.blade.php +++ b/resources/views/invoices/view.blade.php @@ -45,7 +45,11 @@ @endif

     

    - + @if ($account->isPro() && $account->invoice_embed_documents) + @foreach ($invoice->documents as $document) + + @endforeach + @endif