mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2024-11-08 12:12:48 +01:00
Improved handling of various document types; better documents zip
This commit is contained in:
parent
e6056104bd
commit
1c5c45a1e1
@ -46,7 +46,7 @@ class DocumentController extends BaseController
|
||||
|
||||
if($stream){
|
||||
$headers = [
|
||||
'Content-Type' => $document->type,
|
||||
'Content-Type' => Document::$types[$document->type]['mime'],
|
||||
'Content-Length' => $document->size,
|
||||
];
|
||||
|
||||
@ -56,7 +56,7 @@ class DocumentController extends BaseController
|
||||
}
|
||||
else{
|
||||
$response = Response::make($document->getRaw(), 200);
|
||||
$response->header('content-type', $document->type);
|
||||
$response->header('content-type', Document::$types[$document->type]['mime']);
|
||||
}
|
||||
|
||||
return $response;
|
||||
@ -80,9 +80,9 @@ class DocumentController extends BaseController
|
||||
return redirect($direct_url);
|
||||
}
|
||||
|
||||
$extension = pathinfo($document->preview, PATHINFO_EXTENSION);
|
||||
$previewType = pathinfo($document->preview, PATHINFO_EXTENSION);
|
||||
$response = Response::make($document->getRawPreview(), 200);
|
||||
$response->header('content-type', Document::$extensions[$extension]);
|
||||
$response->header('content-type', Document::$types[$previewType]['mime']);
|
||||
|
||||
return $response;
|
||||
}
|
||||
@ -99,7 +99,7 @@ class DocumentController extends BaseController
|
||||
return $response;
|
||||
}
|
||||
|
||||
if(substr($document->type, 0, 6) != 'image/'){
|
||||
if(!$document->isPDFEmbeddable()){
|
||||
return Response::view('error', array('error'=>'Image does not exist!'), 404);
|
||||
}
|
||||
|
||||
|
@ -19,8 +19,7 @@ use App\Ninja\Repositories\ActivityRepository;
|
||||
use App\Events\InvoiceInvitationWasViewed;
|
||||
use App\Events\QuoteInvitationWasViewed;
|
||||
use App\Services\PaymentService;
|
||||
use League\Flysystem\Filesystem;
|
||||
use League\Flysystem\ZipArchive\ZipArchiveAdapter;
|
||||
use Barracuda\ArchiveStream\ZipArchive;
|
||||
|
||||
class PublicClientController extends BaseController
|
||||
{
|
||||
@ -138,8 +137,13 @@ class PublicClientController extends BaseController
|
||||
'phantomjs' => Input::has('phantomjs'),
|
||||
);
|
||||
|
||||
if($account->isPro() && $this->canCreateInvoiceDocsZip($invoice)){
|
||||
if($account->isPro() && $this->canCreateZip()){
|
||||
$zipDocs = $this->getInvoiceZipDocuments($invoice, $size);
|
||||
|
||||
if(count($zipDocs) > 1){
|
||||
$data['documentsZipURL'] = URL::to("client/documents/{$invitation->invitation_key}");
|
||||
$data['documentsZipSize'] = $size;
|
||||
}
|
||||
}
|
||||
|
||||
return View::make('invoices.view', $data);
|
||||
@ -173,6 +177,12 @@ class PublicClientController extends BaseController
|
||||
return $paymentTypes;
|
||||
}
|
||||
|
||||
protected function humanFilesize($bytes, $decimals = 2) {
|
||||
$size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
|
||||
$factor = floor((strlen($bytes) - 1) / 3);
|
||||
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . @$size[$factor];
|
||||
}
|
||||
|
||||
public function download($invitationKey)
|
||||
{
|
||||
if (!$invitation = $this->invoiceRepo->findInvoiceByInvitation($invitationKey)) {
|
||||
@ -391,7 +401,7 @@ class PublicClientController extends BaseController
|
||||
$document = Document::scope($publicId, $invitation->account_id)->first();
|
||||
|
||||
|
||||
if(!$document || substr($document->type, 0, 6) != 'image/'){
|
||||
if(!$document->isPDFEmbeddable()){
|
||||
return Response::view('error', array('error'=>'Image does not exist!'), 404);
|
||||
}
|
||||
|
||||
@ -423,19 +433,43 @@ class PublicClientController extends BaseController
|
||||
return function_exists('gmp_init');
|
||||
}
|
||||
|
||||
protected function canCreateInvoiceDocsZip($invoice){
|
||||
if(!$this->canCreateZip())return false;
|
||||
if(count($invoice->documents) == 1)return false;
|
||||
protected function getInvoiceZipDocuments($invoice, &$size=0){
|
||||
$documents = $invoice->documents->sortBy('size');
|
||||
|
||||
$size = 0;
|
||||
$maxSize = MAX_ZIP_DOCUMENTS_SIZE * 1000;
|
||||
$i = 0;
|
||||
foreach($invoice->documents as $document){
|
||||
if($document->size <= $maxSize)$i++;
|
||||
if($i > 1){
|
||||
return true;
|
||||
$toZip = array();
|
||||
foreach($documents as $document){
|
||||
if($size + $document->size > $maxSize)break;
|
||||
|
||||
if(!empty($toZip[$document->name])){
|
||||
// This name is taken
|
||||
if($toZip[$document->name]->hash != $document->hash){
|
||||
// 2 different files with the same name
|
||||
$nameInfo = pathinfo($document->name);
|
||||
|
||||
for($i = 1;; $i++){
|
||||
$name = $nameInfo['filename'].' ('.$i.').'.$nameInfo['extension'];
|
||||
|
||||
if(empty($toZip[$name])){
|
||||
$toZip[$name] = $document;
|
||||
$size += $document->size;
|
||||
break;
|
||||
} else if ($toZip[$name]->hash == $document->hash){
|
||||
// We're not adding this after all
|
||||
break;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
}
|
||||
else{
|
||||
$toZip[$document->name] = $document;
|
||||
$size += $document->size;
|
||||
}
|
||||
}
|
||||
|
||||
return $toZip;
|
||||
}
|
||||
|
||||
public function getInvoiceDocumentsZip($invitationKey){
|
||||
@ -451,58 +485,19 @@ class PublicClientController extends BaseController
|
||||
return Response::view('error', array('error'=>'No documents'), 404);
|
||||
}
|
||||
|
||||
$documents = $invoice->documents->sortBy('size');
|
||||
|
||||
$size = 0;
|
||||
$maxSize = MAX_ZIP_DOCUMENTS_SIZE * 1000;
|
||||
$toZip = array();
|
||||
foreach($documents as $document){
|
||||
$size += $document->size;
|
||||
if($size > $maxSize)break;
|
||||
|
||||
if(!empty($toZip[$document->name])){
|
||||
$hasSameHash = false;
|
||||
foreach($toZip[$document->name] as $sameName){
|
||||
if($sameName->hash == $document->hash){
|
||||
$hasSameHash = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$hasSameHash){
|
||||
// 2 different files with the same name
|
||||
$toZip[$document->name][] = $document;
|
||||
}
|
||||
else{
|
||||
// We're not adding this after all
|
||||
$size -= $document->size;
|
||||
}
|
||||
}
|
||||
else{
|
||||
$toZip[$document->name] = array($document);
|
||||
}
|
||||
}
|
||||
$toZip = $this->getInvoiceZipDocuments($invoice);
|
||||
|
||||
if(!count($toZip)){
|
||||
return Response::view('error', array('error'=>'No documents small enough'), 404);
|
||||
}
|
||||
|
||||
$zip = new \Barracuda\ArchiveStream\ZipArchive($invitation->account->name.' Invoice '.$invoice->invoice_number.'.zip');
|
||||
$zip = new ZipArchive($invitation->account->name.' Invoice '.$invoice->invoice_number.'.zip');
|
||||
return Response::stream(function() use ($toZip, $zip) {
|
||||
foreach($toZip as $documentsSameName){
|
||||
$i = 0;
|
||||
foreach($documentsSameName as $document){
|
||||
$name = $document->name;
|
||||
|
||||
if($i){
|
||||
$nameInfo = pathinfo($document->name);
|
||||
$name = $nameInfo['filename'].' ('.$i.').'.$nameInfo['extension'];
|
||||
}
|
||||
|
||||
foreach($toZip as $name=>$document){
|
||||
$fileStream = $document->getStream();
|
||||
if($fileStream){
|
||||
$zip->init_file_stream_transfer($name, $document->size);
|
||||
while ($buffer = fread($fileStream, 8192))$zip->stream_file_part($buffer);
|
||||
$zip->init_file_stream_transfer($name, $document->size, array('time'=>$document->created_at->timestamp));
|
||||
while ($buffer = fread($fileStream, 256000))$zip->stream_file_part($buffer);
|
||||
fclose($fileStream);
|
||||
$zip->complete_file_stream();
|
||||
}
|
||||
@ -510,7 +505,6 @@ class PublicClientController extends BaseController
|
||||
$zip->add_file($name, $document->getRaw());
|
||||
}
|
||||
}
|
||||
}
|
||||
$zip->finish();
|
||||
}, 200);
|
||||
}
|
||||
|
@ -5,31 +5,68 @@ use DB;
|
||||
|
||||
class Document extends EntityModel
|
||||
{
|
||||
public static $extensions = array(
|
||||
'png' => 'image/png',
|
||||
'jpg' => 'image/jpeg',
|
||||
'jpeg' => 'image/jpeg',
|
||||
'tiff' => 'image/tiff',
|
||||
'tif' => 'image/tiff',
|
||||
'pdf' => 'application/pdf',
|
||||
'gif' => 'image/gif'
|
||||
public static $extraExtensions = array(
|
||||
'jpg' => 'jpeg',
|
||||
'tif' => 'tiff',
|
||||
);
|
||||
|
||||
public static $allowedMimes = array(// Used by Dropzone.js; does not affect what the server accepts
|
||||
'image/png', 'image/jpeg', 'image/tiff', 'application/pdf', 'image/gif', 'image/vnd.adobe.photoshop', 'text/plain',
|
||||
'application/zip', 'application/msword',
|
||||
'application/excel', 'application/vnd.ms-excel', 'application/x-excel', 'application/x-msexcel',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet','application/postscript', 'image/svg+xml',
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation', 'application/vnd.ms-powerpoint',
|
||||
);
|
||||
|
||||
public static $types = array(
|
||||
'image/png' => array(
|
||||
'extension' => 'png',
|
||||
'png' => array(
|
||||
'mime' => 'image/png',
|
||||
),
|
||||
'image/jpeg' => array(
|
||||
'extension' => 'jpeg',
|
||||
'ai' => array(
|
||||
'mime' => 'application/postscript',
|
||||
),
|
||||
'image/tiff' => array(
|
||||
'extension' => 'tiff',
|
||||
'svg' => array(
|
||||
'mime' => 'image/svg+xml',
|
||||
),
|
||||
'image/gif' => array(
|
||||
'extension' => 'gif',
|
||||
'jpeg' => array(
|
||||
'mime' => 'image/jpeg',
|
||||
),
|
||||
'application/pdf' => array(
|
||||
'extension' => 'pdf',
|
||||
'tiff' => array(
|
||||
'mime' => 'image/tiff',
|
||||
),
|
||||
'pdf' => array(
|
||||
'mime' => 'application/pdf',
|
||||
),
|
||||
'gif' => array(
|
||||
'mime' => 'image/gif',
|
||||
),
|
||||
'psd' => array(
|
||||
'mime' => 'image/vnd.adobe.photoshop',
|
||||
),
|
||||
'txt' => array(
|
||||
'mime' => 'text/plain',
|
||||
),
|
||||
'zip' => array(
|
||||
'mime' => 'application/zip',
|
||||
),
|
||||
'doc' => array(
|
||||
'mime' => 'application/msword',
|
||||
),
|
||||
'xls' => array(
|
||||
'mime' => 'application/vnd.ms-excel',
|
||||
),
|
||||
'ppt' => array(
|
||||
'mime' => 'application/vnd.ms-powerpoint',
|
||||
),
|
||||
'xlsx' => array(
|
||||
'mime' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||
),
|
||||
'docx' => array(
|
||||
'mime' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
),
|
||||
'pptx' => array(
|
||||
'mime' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
),
|
||||
);
|
||||
|
||||
@ -142,11 +179,17 @@ class Document extends EntityModel
|
||||
return url('client/document/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name);
|
||||
}
|
||||
|
||||
public function isPDFEmbeddable(){
|
||||
return $this->type == 'jpeg' || $this->type == 'png' || $this->preview;
|
||||
}
|
||||
|
||||
public function getVFSJSUrl(){
|
||||
if(!$this->isPDFEmbeddable())return null;
|
||||
return url('document/js/'.$this->public_id.'/'.$this->name.'.js');
|
||||
}
|
||||
|
||||
public function getClientVFSJSUrl(){
|
||||
if(!$this->isPDFEmbeddable())return null;
|
||||
return url('client/document/js/'.$this->public_id.'/'.$this->name.'.js');
|
||||
}
|
||||
|
||||
|
@ -61,14 +61,22 @@ class DocumentRepository extends BaseRepository
|
||||
$uploaded = $input['file'];
|
||||
|
||||
$extension = strtolower($uploaded->extension());
|
||||
if(empty(Document::$extensions[$extension])){
|
||||
if(empty(Document::$types[$extension]) && !empty(Document::$extraExtensions[$extension])){
|
||||
$documentType = Document::$extraExtensions[$extension];
|
||||
}
|
||||
else{
|
||||
$documentType = $extension;
|
||||
}
|
||||
|
||||
if(empty(Document::$types[$documentType])){
|
||||
return Response::json([
|
||||
'error' => 'Unsupported extension',
|
||||
'code' => 400
|
||||
], 400);
|
||||
}
|
||||
|
||||
$documentType = Document::$extensions[$extension];
|
||||
$documentTypeData = Document::$types[$documentType];
|
||||
|
||||
$filePath = $uploaded->path();
|
||||
$name = $uploaded->getClientOriginalName();
|
||||
$size = filesize($filePath);
|
||||
@ -80,10 +88,10 @@ class DocumentRepository extends BaseRepository
|
||||
], 400);
|
||||
}
|
||||
|
||||
$documentTypeData = Document::$types[$documentType];
|
||||
|
||||
|
||||
$hash = sha1_file($filePath);
|
||||
$filename = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentTypeData['extension'];
|
||||
$filename = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentType;
|
||||
|
||||
$document = Document::createNew();
|
||||
$disk = $document->getDisk();
|
||||
@ -94,20 +102,20 @@ class DocumentRepository extends BaseRepository
|
||||
}
|
||||
|
||||
// This is an image; check if we need to create a preview
|
||||
if(in_array($documentType, array('image/jpeg','image/png','image/gif','image/bmp','image/tiff'))){
|
||||
if(in_array($documentType, array('jpeg','png','gif','bmp','tiff','psd'))){
|
||||
$makePreview = false;
|
||||
$imageSize = getimagesize($filePath);
|
||||
$width = $imageSize[0];
|
||||
$height = $imageSize[1];
|
||||
$imgManagerConfig = array();
|
||||
if(in_array($documentType, array('image/gif','image/bmp','image/tiff'))){
|
||||
if(in_array($documentType, array('gif','bmp','tiff','psd'))){
|
||||
// Needs to be converted
|
||||
$makePreview = true;
|
||||
} else if($width > DOCUMENT_PREVIEW_SIZE || $height > DOCUMENT_PREVIEW_SIZE){
|
||||
$makePreview = true;
|
||||
}
|
||||
|
||||
if($documentType == 'image/bmp' || $documentType == 'image/tiff'){
|
||||
if(in_array($documentType,array('bmp','tiff','psd'))){
|
||||
if(!class_exists('Imagick')){
|
||||
// Cant't read this
|
||||
$makePreview = false;
|
||||
@ -117,13 +125,13 @@ class DocumentRepository extends BaseRepository
|
||||
}
|
||||
|
||||
if($makePreview){
|
||||
$previewType = 'jpg';
|
||||
if(in_array($documentType, array('image/png','image/gif','image/bmp','image/tiff'))){
|
||||
$previewType = 'jpeg';
|
||||
if(in_array($documentType, array('png','gif','tiff','psd'))){
|
||||
// Has transparency
|
||||
$previewType = 'png';
|
||||
}
|
||||
|
||||
$document->preview = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentTypeData['extension'].'.x'.DOCUMENT_PREVIEW_SIZE.'.'.$previewType;
|
||||
$document->preview = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentType.'.x'.DOCUMENT_PREVIEW_SIZE.'.'.$previewType;
|
||||
if(!$disk->exists($document->preview)){
|
||||
// We haven't created a preview yet
|
||||
$imgManager = new ImageManager($imgManagerConfig);
|
||||
@ -170,7 +178,7 @@ class DocumentRepository extends BaseRepository
|
||||
$doc_array = $document->toArray();
|
||||
|
||||
if(!empty($base64)){
|
||||
$mime = !empty($previewType)?Document::$extensions[$previewType]:$documentType;
|
||||
$mime = Document::$types[!empty($previewType)?$previewType:$documentType]['mime'];
|
||||
$doc_array['base64'] = 'data:'.$mime.';base64,'.$base64;
|
||||
}
|
||||
|
||||
|
@ -159,6 +159,12 @@ class AppServiceProvider extends ServiceProvider {
|
||||
return $str . '</ol>';
|
||||
});
|
||||
|
||||
Form::macro('human_filesize', function($bytes, $decimals = 1) {
|
||||
$size = array('B','kB','MB','GB','TB','PB','EB','ZB','YB');
|
||||
$factor = floor((strlen($bytes) - 1) / 3);
|
||||
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor];
|
||||
});
|
||||
|
||||
Validator::extend('positive', function($attribute, $value, $parameters) {
|
||||
return Utils::parseFloat($value) >= 0;
|
||||
});
|
||||
|
@ -31404,10 +31404,10 @@ NINJA.invoiceDocuments = function(invoice) {
|
||||
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)path = 'docs/'+document.public_id+'/'+document.name;
|
||||
if(path && (window.pdfMake.vfs[path] || document.base64)){
|
||||
// Only embed if we actually have an image for it
|
||||
if(j%3==0){
|
||||
stackItem = {columns:[]};
|
||||
stack.push(stackItem);
|
||||
|
@ -412,10 +412,10 @@ NINJA.invoiceDocuments = function(invoice) {
|
||||
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)path = 'docs/'+document.public_id+'/'+document.name;
|
||||
if(path && (window.pdfMake.vfs[path] || document.base64)){
|
||||
// Only embed if we actually have an image for it
|
||||
if(j%3==0){
|
||||
stackItem = {columns:[]};
|
||||
stack.push(stackItem);
|
||||
|
@ -1107,7 +1107,7 @@ $LANG = array(
|
||||
'invoice_embed_documents' => 'Embed Documents',
|
||||
'invoice_embed_documents_help' => 'Include attached images in the invoice.',
|
||||
'document_email_attachment' => 'Attach Documents',
|
||||
'download_documents' => 'Download Documents',
|
||||
'download_documents' => 'Download Documents (:size)',
|
||||
);
|
||||
|
||||
return $LANG;
|
||||
|
@ -930,7 +930,7 @@
|
||||
params:{
|
||||
_token:"{{ Session::getToken() }}"
|
||||
},
|
||||
acceptedFiles:{!! json_encode(implode(',',array_keys(\App\Models\Document::$types))) !!},
|
||||
acceptedFiles:{!! json_encode(implode(',',\App\Models\Document::$allowedMimes)) !!},
|
||||
addRemoveLinks:true,
|
||||
maxFileSize:{{floatval(MAX_DOCUMENT_SIZE/1000)}},
|
||||
dictDefaultMessage:{!! json_encode(trans('texts.document_upload_message')) !!}
|
||||
@ -955,9 +955,12 @@
|
||||
|
||||
dropzone.emit('addedfile', mockFile);
|
||||
dropzone.emit('complete', mockFile);
|
||||
if(document.type().match(/image.*/)){
|
||||
if(document.preview_url()){
|
||||
dropzone.emit('thumbnail', mockFile, document.preview_url()||document.url());
|
||||
}
|
||||
else if(document.type()=='jpeg' || document.type()=='png' || document.type()=='svg'){
|
||||
dropzone.emit('thumbnail', mockFile, document.url());
|
||||
}
|
||||
dropzone.files.push(mockFile);
|
||||
}
|
||||
@endif
|
||||
@ -1005,7 +1008,9 @@
|
||||
}
|
||||
|
||||
function createInvoiceModel() {
|
||||
var invoice = ko.toJS(window.model).invoice;
|
||||
var model = ko.toJS(window.model);
|
||||
if(!model)return;
|
||||
var invoice = model.invoice;
|
||||
invoice.is_pro = {{ Auth::user()->isPro() ? 'true' : 'false' }};
|
||||
invoice.is_quote = {{ $entityType == ENTITY_QUOTE ? 'true' : 'false' }};
|
||||
invoice.contact = _.findWhere(invoice.client.contacts, {send_invoice: true});
|
||||
@ -1335,13 +1340,19 @@
|
||||
file.public_id = response.document.public_id
|
||||
model.invoice().documents()[file.index].update(response.document);
|
||||
refreshPDF(true);
|
||||
|
||||
if(response.document.preview_url){
|
||||
dropzone.emit('thumbnail', file, response.document.preview_url);
|
||||
}
|
||||
}
|
||||
@endif
|
||||
|
||||
</script>
|
||||
@if ($account->isPro() && $account->invoice_embed_documents)
|
||||
@foreach ($invoice->documents as $document)
|
||||
@if($document->isPDFEmbeddable())
|
||||
<script src="{{ $document->getVFSJSUrl() }}" type="text/javascript" async></script>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
|
||||
|
@ -59,7 +59,9 @@
|
||||
|
||||
@if (Auth::user()->account->isPro() && Auth::user()->account->invoice_embed_documents)
|
||||
@foreach ($invoice->documents as $document)
|
||||
@if($document->isPDFEmbeddable())
|
||||
<script src="{{ $document->getVFSJSUrl() }}" type="text/javascript" async></script>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
@stop
|
@ -43,7 +43,7 @@
|
||||
</div>
|
||||
<div class="pull-left">
|
||||
@if(!empty($documentsZipURL))
|
||||
{!! Button::normal(trans('texts.download_documents'))->asLinkTo($documentsZipURL)->large() !!}
|
||||
{!! Button::normal(trans('texts.download_documents', array('size'=>Form::human_filesize($documentsZipSize))))->asLinkTo($documentsZipURL)->large() !!}
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
@ -54,7 +54,7 @@
|
||||
<h3>{{ trans('texts.documents_header') }}</h3>
|
||||
<ul>
|
||||
@foreach ($invoice->documents as $document)
|
||||
<li><a target="_blank" href="{{ $document->getClientUrl($invitation) }}">{{$document->name}}</a></li>
|
||||
<li><a target="_blank" href="{{ $document->getClientUrl($invitation) }}">{{$document->name}} ({{Form::human_filesize($document->size)}})</a></li>
|
||||
@endforeach
|
||||
</ul>
|
||||
</div>
|
||||
@ -62,7 +62,9 @@
|
||||
|
||||
@if ($account->isPro() && $account->invoice_embed_documents)
|
||||
@foreach ($invoice->documents as $document)
|
||||
@if($document->isPDFEmbeddable())
|
||||
<script src="{{ $document->getClientVFSJSUrl() }}" type="text/javascript" {{ Input::has('phantomjs')?'':'async' }}></script>
|
||||
@endif
|
||||
@endforeach
|
||||
@endif
|
||||
<script type="text/javascript">
|
||||
|
Loading…
Reference in New Issue
Block a user