mirror of
https://github.com/invoiceninja/invoiceninja.git
synced 2025-07-09 03:14:30 -04:00
Support saving invoice_id and expense_id with the document
This commit is contained in:
parent
51d9b2b427
commit
17eb2a7a79
@ -33,7 +33,7 @@ class DocumentAPIController extends BaseAPIController
|
||||
|
||||
public function store(CreateDocumentRequest $request)
|
||||
{
|
||||
$document = $this->documentRepo->upload($request->file);
|
||||
$document = $this->documentRepo->upload($request->all());
|
||||
|
||||
return $this->itemResponse($document);
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ class DocumentController extends BaseController
|
||||
|
||||
public function postUpload(CreateDocumentRequest $request)
|
||||
{
|
||||
$result = $this->documentRepo->upload($request->file, $doc_array);
|
||||
$result = $this->documentRepo->upload($request->all(), $doc_array);
|
||||
|
||||
if(is_string($result)){
|
||||
return Response::json([
|
||||
|
@ -1,7 +1,15 @@
|
||||
<?php namespace App\Http\Requests;
|
||||
|
||||
use App\Models\Invoice;
|
||||
use App\Models\Expense;
|
||||
|
||||
class CreateDocumentRequest extends DocumentRequest
|
||||
{
|
||||
protected $autoload = [
|
||||
ENTITY_INVOICE,
|
||||
ENTITY_EXPENSE,
|
||||
];
|
||||
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*
|
||||
@ -9,7 +17,19 @@ class CreateDocumentRequest extends DocumentRequest
|
||||
*/
|
||||
public function authorize()
|
||||
{
|
||||
return $this->user()->can('create', ENTITY_DOCUMENT) && $this->user()->hasFeature(FEATURE_DOCUMENTS);
|
||||
if ( ! $this->user()->hasFeature(FEATURE_DOCUMENTS)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->invoice && $this->user()->cannot('edit', $this->invoice)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($this->expense && $this->user()->cannot('edit', $this->expense)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->user()->can('create', ENTITY_DOCUMENT);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -24,21 +44,4 @@ class CreateDocumentRequest extends DocumentRequest
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sanitize input before validation.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
/*
|
||||
public function sanitize()
|
||||
{
|
||||
$input = $this->all();
|
||||
|
||||
$input['phone'] = 'test123';
|
||||
|
||||
$this->replace($input);
|
||||
|
||||
return $this->all();
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
@ -5,6 +5,9 @@ use Illuminate\Foundation\Http\FormRequest;
|
||||
// https://laracasts.com/discuss/channels/general-discussion/laravel-5-modify-input-before-validation/replies/34366
|
||||
abstract class Request extends FormRequest {
|
||||
|
||||
// populate in subclass to auto load record
|
||||
protected $autoload = [];
|
||||
|
||||
/**
|
||||
* Validate the input.
|
||||
*
|
||||
@ -25,11 +28,24 @@ abstract class Request extends FormRequest {
|
||||
*/
|
||||
protected function sanitizeInput()
|
||||
{
|
||||
if (method_exists($this, 'sanitize'))
|
||||
{
|
||||
return $this->container->call([$this, 'sanitize']);
|
||||
if (method_exists($this, 'sanitize')) {
|
||||
$input = $this->container->call([$this, 'sanitize']);
|
||||
} else {
|
||||
$input = $this->all();
|
||||
}
|
||||
|
||||
// autoload referenced entities
|
||||
foreach ($this->autoload as $entityType) {
|
||||
if ($id = $this->input("{$entityType}_public_id") ?: $this->input("{$entityType}_id")) {
|
||||
$class = "App\\Models\\" . ucwords($entityType);
|
||||
$entity = $class::scope($id)->firstOrFail();
|
||||
$input[$entityType] = $entity;
|
||||
$input[$entityType . '_id'] = $entity->id;
|
||||
}
|
||||
}
|
||||
|
||||
$this->replace($input);
|
||||
|
||||
return $this->all();
|
||||
}
|
||||
}
|
||||
|
@ -6,20 +6,25 @@ use Auth;
|
||||
|
||||
class Document extends EntityModel
|
||||
{
|
||||
protected $fillable = [
|
||||
'invoice_id',
|
||||
'expense_id',
|
||||
];
|
||||
|
||||
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/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(
|
||||
'png' => array(
|
||||
'mime' => 'image/png',
|
||||
@ -70,18 +75,18 @@ class Document extends EntityModel
|
||||
'mime' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
public function fill(array $attributes)
|
||||
{
|
||||
parent::fill($attributes);
|
||||
|
||||
|
||||
if(empty($this->attributes['disk'])){
|
||||
$this->attributes['disk'] = env('DOCUMENT_FILESYSTEM', 'documents');
|
||||
}
|
||||
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
public function account()
|
||||
{
|
||||
return $this->belongsTo('App\Models\Account');
|
||||
@ -101,7 +106,7 @@ class Document extends EntityModel
|
||||
{
|
||||
return $this->belongsTo('App\Models\Invoice')->withTrashed();
|
||||
}
|
||||
|
||||
|
||||
public function getDisk(){
|
||||
return Storage::disk(!empty($this->disk)?$this->disk:env('DOCUMENT_FILESYSTEM', 'documents'));
|
||||
}
|
||||
@ -110,19 +115,19 @@ class Document extends EntityModel
|
||||
{
|
||||
$this->attributes['disk'] = $value?$value:env('DOCUMENT_FILESYSTEM', 'documents');
|
||||
}
|
||||
|
||||
|
||||
public function getDirectUrl(){
|
||||
return static::getDirectFileUrl($this->path, $this->getDisk());
|
||||
}
|
||||
|
||||
|
||||
public function getDirectPreviewUrl(){
|
||||
return $this->preview?static::getDirectFileUrl($this->preview, $this->getDisk(), true):null;
|
||||
}
|
||||
|
||||
|
||||
public static function getDirectFileUrl($path, $disk, $prioritizeSpeed = false){
|
||||
$adapter = $disk->getAdapter();
|
||||
$fullPath = $adapter->applyPathPrefix($path);
|
||||
|
||||
|
||||
if($adapter instanceof \League\Flysystem\AwsS3v3\AwsS3Adapter) {
|
||||
$client = $adapter->getClient();
|
||||
$command = $client->getCommand('GetObject', [
|
||||
@ -136,12 +141,12 @@ class Document extends EntityModel
|
||||
$secret = env('RACKSPACE_TEMP_URL_SECRET');
|
||||
if($secret){
|
||||
$object = $adapter->getContainer()->getObject($fullPath);
|
||||
|
||||
|
||||
if(env('RACKSPACE_TEMP_URL_SECRET_SET')){
|
||||
// Go ahead and set the secret too
|
||||
$object->getService()->getAccount()->setTempUrlSecret($secret);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$url = $object->getUrl();
|
||||
$expiry = strtotime('+10 minutes');
|
||||
$urlPath = urldecode($url->getPath());
|
||||
@ -150,64 +155,64 @@ class Document extends EntityModel
|
||||
return sprintf('%s?temp_url_sig=%s&temp_url_expires=%d', $url, $hash, $expiry);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public function getRaw(){
|
||||
$disk = $this->getDisk();
|
||||
|
||||
|
||||
return $disk->get($this->path);
|
||||
}
|
||||
|
||||
|
||||
public function getStream(){
|
||||
$disk = $this->getDisk();
|
||||
|
||||
|
||||
return $disk->readStream($this->path);
|
||||
}
|
||||
|
||||
|
||||
public function getRawPreview(){
|
||||
$disk = $this->getDisk();
|
||||
|
||||
|
||||
return $disk->get($this->preview);
|
||||
}
|
||||
|
||||
|
||||
public function getUrl(){
|
||||
return url('documents/'.$this->public_id.'/'.$this->name);
|
||||
}
|
||||
|
||||
|
||||
public function getClientUrl($invitation){
|
||||
return url('client/documents/'.$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('documents/js/'.$this->public_id.'/'.$this->name.'.js');
|
||||
}
|
||||
|
||||
|
||||
public function getClientVFSJSUrl(){
|
||||
if(!$this->isPDFEmbeddable())return null;
|
||||
return url('client/documents/js/'.$this->public_id.'/'.$this->name.'.js');
|
||||
}
|
||||
|
||||
|
||||
public function getPreviewUrl(){
|
||||
return $this->preview?url('documents/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)):null;
|
||||
}
|
||||
|
||||
|
||||
public function toArray()
|
||||
{
|
||||
$array = parent::toArray();
|
||||
|
||||
|
||||
if(empty($this->visible) || in_array('url', $this->visible))$array['url'] = $this->getUrl();
|
||||
if(empty($this->visible) || in_array('preview_url', $this->visible))$array['preview_url'] = $this->getPreviewUrl();
|
||||
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
|
||||
public function cloneDocument(){
|
||||
$document = Document::createNew($this);
|
||||
$document->path = $this->path;
|
||||
@ -219,7 +224,7 @@ class Document extends EntityModel
|
||||
$document->size = $this->size;
|
||||
$document->width = $this->width;
|
||||
$document->height = $this->height;
|
||||
|
||||
|
||||
return $document;
|
||||
}
|
||||
}
|
||||
@ -230,11 +235,11 @@ Document::deleted(function ($document) {
|
||||
->where('documents.path', '=', $document->path)
|
||||
->where('documents.disk', '=', $document->disk)
|
||||
->count();
|
||||
|
||||
|
||||
if(!$same_path_count){
|
||||
$document->getDisk()->delete($document->path);
|
||||
}
|
||||
|
||||
|
||||
if($document->preview){
|
||||
$same_preview_count = DB::table('documents')
|
||||
->where('documents.account_id', '=', $document->account_id)
|
||||
@ -245,5 +250,5 @@ Document::deleted(function ($document) {
|
||||
$document->getDisk()->delete($document->preview);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
|
@ -57,8 +57,9 @@ class DocumentRepository extends BaseRepository
|
||||
return $query;
|
||||
}
|
||||
|
||||
public function upload($uploaded, &$doc_array=null)
|
||||
public function upload($data, &$doc_array=null)
|
||||
{
|
||||
$uploaded = $data['file'];
|
||||
$extension = strtolower($uploaded->getClientOriginalExtension());
|
||||
if(empty(Document::$types[$extension]) && !empty(Document::$extraExtensions[$extension])){
|
||||
$documentType = Document::$extraExtensions[$extension];
|
||||
@ -81,12 +82,17 @@ class DocumentRepository extends BaseRepository
|
||||
return 'File too large';
|
||||
}
|
||||
|
||||
|
||||
// don't allow a document to be linked to both an invoice and an expense
|
||||
if (array_get($data, 'invoice_id') && array_get($data, 'expense_id')) {
|
||||
unset($data['expense_id']);
|
||||
}
|
||||
|
||||
$hash = sha1_file($filePath);
|
||||
$filename = \Auth::user()->account->account_key.'/'.$hash.'.'.$documentType;
|
||||
|
||||
$document = Document::createNew();
|
||||
$document->fill($data);
|
||||
|
||||
$disk = $document->getDisk();
|
||||
if(!$disk->exists($filename)){// Have we already stored the same file
|
||||
$stream = fopen($filePath, 'r');
|
||||
|
@ -29,8 +29,8 @@ class AppServiceProvider extends ServiceProvider {
|
||||
else{
|
||||
$contents = $image;
|
||||
}
|
||||
|
||||
return 'data:image/jpeg;base64,' . base64_encode($contents);
|
||||
|
||||
return 'data:image/jpeg;base64,' . base64_encode($contents);
|
||||
});
|
||||
|
||||
Form::macro('nav_link', function($url, $text, $url2 = '', $extra = '') {
|
||||
@ -58,11 +58,11 @@ class AppServiceProvider extends ServiceProvider {
|
||||
|
||||
$str = '<li class="dropdown '.$class.'">
|
||||
<a href="'.URL::to($types).'" class="dropdown-toggle">'.trans("texts.$types").'</a>';
|
||||
|
||||
|
||||
$items = [];
|
||||
|
||||
|
||||
if($user->can('create', $type))$items[] = '<li><a href="'.URL::to($types.'/create').'">'.trans("texts.new_$type").'</a></li>';
|
||||
|
||||
|
||||
if ($type == ENTITY_INVOICE) {
|
||||
if(!empty($items))$items[] = '<li class="divider"></li>';
|
||||
$items[] = '<li><a href="'.URL::to('recurring_invoices').'">'.trans("texts.recurring_invoices").'</a></li>';
|
||||
@ -81,7 +81,7 @@ class AppServiceProvider extends ServiceProvider {
|
||||
$items[] = '<li><a href="'.URL::to('vendors').'">'.trans("texts.vendors").'</a></li>';
|
||||
if($user->can('create', ENTITY_VENDOR))$items[] = '<li><a href="'.URL::to('vendors/create').'">'.trans("texts.new_vendor").'</a></li>';
|
||||
}
|
||||
|
||||
|
||||
if(!empty($items)){
|
||||
$str.= '<ul class="dropdown-menu" id="menu1">'.implode($items).'</ul>';
|
||||
}
|
||||
@ -157,14 +157,14 @@ 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);
|
||||
if($factor == 0)$decimals=0;// There aren't fractional bytes
|
||||
return sprintf("%.{$decimals}f", $bytes / pow(1024, $factor)) . ' ' . @$size[$factor];
|
||||
});
|
||||
|
||||
|
||||
Validator::extend('positive', function($attribute, $value, $parameters) {
|
||||
return Utils::parseFloat($value) >= 0;
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user