mirror of
				https://github.com/invoiceninja/invoiceninja.git
				synced 2025-11-03 23:07:32 -05:00 
			
		
		
		
	Improve proposal PDF rendering
This commit is contained in:
		
							parent
							
								
									d5bf19ad98
								
							
						
					
					
						commit
						c8d44ba1b2
					
				@ -50,4 +50,21 @@ class BaseController extends Controller
 | 
				
			|||||||
            return redirect("{$entityTypes}");
 | 
					            return redirect("{$entityTypes}");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected function downloadResponse($filename, $contents, $type = 'application/pdf')
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        header('Content-Type: ' . $type);
 | 
				
			||||||
 | 
					        header('Content-Length: ' . strlen($contents));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (! request()->debug) {
 | 
				
			||||||
 | 
					            header('Content-disposition: attachment; filename="' . $filename . '"');
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        header('Cache-Control: public, must-revalidate, max-age=0');
 | 
				
			||||||
 | 
					        header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        echo $contents;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        exit;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -2,11 +2,11 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
namespace App\Http\Controllers;
 | 
					namespace App\Http\Controllers;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use mPDF;
 | 
					 | 
				
			||||||
use App\Models\Account;
 | 
					use App\Models\Account;
 | 
				
			||||||
use App\Models\Document;
 | 
					use App\Models\Document;
 | 
				
			||||||
use App\Models\Invitation;
 | 
					use App\Models\Invitation;
 | 
				
			||||||
use App\Ninja\Repositories\ProposalRepository;
 | 
					use App\Ninja\Repositories\ProposalRepository;
 | 
				
			||||||
 | 
					use App\Jobs\ConvertProposalToPdf;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ClientPortalProposalController extends BaseController
 | 
					class ClientPortalProposalController extends BaseController
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -39,8 +39,12 @@ class ClientPortalProposalController extends BaseController
 | 
				
			|||||||
            'proposalInvitation' => $invitation,
 | 
					            'proposalInvitation' => $invitation,
 | 
				
			||||||
        ];
 | 
					        ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if (request()->phantomjs) {
 | 
				
			||||||
 | 
					            return $proposal->present()->htmlDocument;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
            return view('invited.proposal', $data);
 | 
					            return view('invited.proposal', $data);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function downloadProposal($invitationKey)
 | 
					    public function downloadProposal($invitationKey)
 | 
				
			||||||
    {
 | 
					    {
 | 
				
			||||||
@ -50,9 +54,9 @@ class ClientPortalProposalController extends BaseController
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        $proposal = $invitation->proposal;
 | 
					        $proposal = $invitation->proposal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $mpdf = new mPDF();
 | 
					        $pdf = dispatch(new ConvertProposalToPdf($proposal));
 | 
				
			||||||
        $mpdf->WriteHTML($proposal->present()->htmlDocument);
 | 
					
 | 
				
			||||||
        $mpdf->Output($proposal->present()->filename, 'D');
 | 
					        $this->downloadResponse($proposal->getFilename(), $pdf);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public function getProposalImage($accountKey, $documentKey)
 | 
					    public function getProposalImage($accountKey, $documentKey)
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,7 @@ use App\Http\Requests\CreateProposalRequest;
 | 
				
			|||||||
use App\Http\Requests\ProposalRequest;
 | 
					use App\Http\Requests\ProposalRequest;
 | 
				
			||||||
use App\Http\Requests\UpdateProposalRequest;
 | 
					use App\Http\Requests\UpdateProposalRequest;
 | 
				
			||||||
use App\Jobs\SendInvoiceEmail;
 | 
					use App\Jobs\SendInvoiceEmail;
 | 
				
			||||||
 | 
					use App\Jobs\ConvertProposalToPdf;
 | 
				
			||||||
use App\Models\Invoice;
 | 
					use App\Models\Invoice;
 | 
				
			||||||
use App\Models\Proposal;
 | 
					use App\Models\Proposal;
 | 
				
			||||||
use App\Models\ProposalTemplate;
 | 
					use App\Models\ProposalTemplate;
 | 
				
			||||||
@ -17,7 +18,6 @@ use Auth;
 | 
				
			|||||||
use Input;
 | 
					use Input;
 | 
				
			||||||
use Session;
 | 
					use Session;
 | 
				
			||||||
use View;
 | 
					use View;
 | 
				
			||||||
use mPDF;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
class ProposalController extends BaseController
 | 
					class ProposalController extends BaseController
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
@ -167,14 +167,8 @@ class ProposalController extends BaseController
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        $proposal = $request->entity();
 | 
					        $proposal = $request->entity();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        $mpdf = new mPDF();
 | 
					        $pdf = dispatch(new ConvertProposalToPdf($proposal));
 | 
				
			||||||
        //$mpdf->showImageErrors = config('app.debug');
 | 
					 | 
				
			||||||
        $mpdf->WriteHTML($proposal->present()->htmlDocument);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if ($request->debug) {
 | 
					        $this->downloadResponse($proposal->getFilename(), $pdf);
 | 
				
			||||||
            $mpdf->Output();
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
            $mpdf->Output($proposal->present()->filename, 'D');
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										25
									
								
								app/Jobs/ConvertProposalToPdf.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								app/Jobs/ConvertProposalToPdf.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,25 @@
 | 
				
			|||||||
 | 
					<?php
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace App\Jobs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use App\Jobs\Job;
 | 
				
			||||||
 | 
					use App\Libraries\CurlUtils;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ConvertProposalToPdf extends Job
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    public function __construct($proposal)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $this->proposal = $proposal;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function handle()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $proposal = $this->proposal;
 | 
				
			||||||
 | 
					        $url = $proposal->getHeadlessLink();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $filename = sprintf('%s/storage/app/%s.pdf', base_path(), strtolower(str_random(RANDOM_KEY_LENGTH)));
 | 
				
			||||||
 | 
					        $pdf = CurlUtils::renderPDF($url, $filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $pdf;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -66,4 +66,32 @@ class CurlUtils
 | 
				
			|||||||
            return false;
 | 
					            return false;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public static function renderPDF($url, $filename)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        if (! $path = env('PHANTOMJS_BIN_PATH')) {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $client = Client::getInstance();
 | 
				
			||||||
 | 
					        $client->isLazy();
 | 
				
			||||||
 | 
					        $client->getEngine()->addOption("--load-images=true");
 | 
				
			||||||
 | 
					        $client->getEngine()->setPath($path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $request = $client->getMessageFactory()->createPdfRequest($url, 'GET');
 | 
				
			||||||
 | 
					        $request->setOutputFile($filename);
 | 
				
			||||||
 | 
					        //$request->setOrientation('landscape');
 | 
				
			||||||
 | 
					        $request->setMargin('0');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        $response = $client->getMessageFactory()->createResponse();
 | 
				
			||||||
 | 
					        $client->send($request, $response);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if ($response->getStatus() === 200) {
 | 
				
			||||||
 | 
					            $pdf = file_get_contents($filename);
 | 
				
			||||||
 | 
					            unlink($filename);
 | 
				
			||||||
 | 
					            return $pdf;
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            return false;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -96,6 +96,25 @@ class Proposal extends EntityModel
 | 
				
			|||||||
    {
 | 
					    {
 | 
				
			||||||
        return $this->invoice->invoice_number;
 | 
					        return $this->invoice->invoice_number;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getLink($forceOnsite = false, $forcePlain = false)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $invitation = $this->invitations->first();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return $invitation->getLink('proposal', $forceOnsite, $forcePlain);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getHeadlessLink()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        return sprintf('%s?phantomjs=true&phantomjs_secret=%s', $this->getLink(true, true), env('PHANTOMJS_SECRET'));
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public function getFilename($extension = 'pdf')
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        $entityType = $this->getEntityType();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return trans('texts.proposal') . '_' . $this->invoice->invoice_number . '.' . $extension;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Proposal::creating(function ($project) {
 | 
					Proposal::creating(function ($project) {
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										7
									
								
								storage/templates/clean.css
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										7
									
								
								storage/templates/clean.css
									
									
									
									
										vendored
									
									
								
							@ -11,6 +11,7 @@ body {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.blue-upper {
 | 
					.blue-upper {
 | 
				
			||||||
 | 
						padding-bottom: 8px;
 | 
				
			||||||
	font-size: 11px;
 | 
						font-size: 11px;
 | 
				
			||||||
	letter-spacing: 3px;
 | 
						letter-spacing: 3px;
 | 
				
			||||||
	text-transform: uppercase;
 | 
						text-transform: uppercase;
 | 
				
			||||||
@ -94,7 +95,7 @@ a.button:hover {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
table td {
 | 
					table td {
 | 
				
			||||||
	padding: 20px 30px;
 | 
						padding: 20px;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
tr.top-header {
 | 
					tr.top-header {
 | 
				
			||||||
@ -122,10 +123,6 @@ tr.top-header p {
 | 
				
			|||||||
	padding: 0 0 120px 0;
 | 
						padding: 0 0 120px 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.card-content {
 | 
					 | 
				
			||||||
	padding: 80px 30px;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
tr.block-quote {
 | 
					tr.block-quote {
 | 
				
			||||||
	margin: 50px 0 ;
 | 
						margin: 50px 0 ;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user