'jpeg', 'tif' => 'tiff', ]; /** * @var array */ public static $allowedMimes = [// 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/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', ]; /** * @var array */ public static $types = [ 'png' => [ 'mime' => 'image/png', ], 'ai' => [ 'mime' => 'application/postscript', ], 'svg' => [ 'mime' => 'image/svg+xml', ], 'jpeg' => [ 'mime' => 'image/jpeg', ], 'tiff' => [ 'mime' => 'image/tiff', ], 'pdf' => [ 'mime' => 'application/pdf', ], 'gif' => [ 'mime' => 'image/gif', ], 'psd' => [ 'mime' => 'image/vnd.adobe.photoshop', ], 'txt' => [ 'mime' => 'text/plain', ], 'doc' => [ 'mime' => 'application/msword', ], 'xls' => [ 'mime' => 'application/vnd.ms-excel', ], 'ppt' => [ 'mime' => 'application/vnd.ms-powerpoint', ], 'xlsx' => [ 'mime' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', ], 'docx' => [ 'mime' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', ], 'pptx' => [ 'mime' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', ], ]; /** * @param array $attributes * * @return $this */ public function fill(array $attributes) { parent::fill($attributes); if (empty($this->attributes['disk'])) { $this->attributes['disk'] = env('DOCUMENT_FILESYSTEM', 'documents'); } return $this; } /** * @return \Illuminate\Database\Eloquent\Relations\BelongsTo */ public function account() { return $this->belongsTo('App\Models\Account'); } /** * @return mixed */ public function user() { return $this->belongsTo('App\Models\User')->withTrashed(); } /** * @return mixed */ public function expense() { return $this->belongsTo('App\Models\Expense')->withTrashed(); } /** * @return mixed */ public function invoice() { return $this->belongsTo('App\Models\Invoice')->withTrashed(); } /** * @return mixed */ public function getDisk() { return Storage::disk(! empty($this->disk) ? $this->disk : env('DOCUMENT_FILESYSTEM', 'documents')); } /** * @param $value */ public function setDiskAttribute($value) { $this->attributes['disk'] = $value ? $value : env('DOCUMENT_FILESYSTEM', 'documents'); } /** * @return null|string */ public function getDirectUrl() { return static::getDirectFileUrl($this->path, $this->getDisk()); } /** * @return null|string */ public function getDirectPreviewUrl() { return $this->preview ? static::getDirectFileUrl($this->preview, $this->getDisk(), true) : null; } /** * @param $path * @param $disk * @param bool $prioritizeSpeed * * @throws \OpenCloud\Common\Exceptions\NoNameError * * @return null|string */ 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', [ 'Bucket' => $adapter->getBucket(), 'Key' => $fullPath, ]); return (string) $client->createPresignedRequest($command, '+10 minutes')->getUri(); } elseif (! $prioritizeSpeed // Rackspace temp URLs are slow, so we don't use them for previews && $adapter instanceof \League\Flysystem\Rackspace\RackspaceAdapter) { $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()); $body = sprintf("%s\n%d\n%s", 'GET', $expiry, $urlPath); $hash = hash_hmac('sha1', $body, $secret); return sprintf('%s?temp_url_sig=%s&temp_url_expires=%d', $url, $hash, $expiry); } } return null; } /** * @return mixed */ public function getRaw() { $disk = $this->getDisk(); return $disk->get($this->path); } /** * @return mixed */ public function getRawCached() { $key = 'image:' . $this->path; if ($image = cache($key)) { // do nothing } else { $image = $this->getRaw(); cache([$key => $image], 120); } return $image; } /** * @return mixed */ public function getStream() { $disk = $this->getDisk(); return $disk->readStream($this->path); } /** * @return mixed */ public function getRawPreview() { $disk = $this->getDisk(); return $disk->get($this->preview); } /** * @return \Illuminate\Contracts\Routing\UrlGenerator|string */ public function getUrl() { return url('documents/'.$this->public_id.'/'.$this->name); } /** * @param $invitation * * @return \Illuminate\Contracts\Routing\UrlGenerator|string */ public function getClientUrl($invitation) { return url('client/documents/'.$invitation->invitation_key.'/'.$this->public_id.'/'.$this->name); } public function getProposalUrl() { if (! $this->is_proposal || ! $this->document_key) { return ''; } return url('proposal/image/'. $this->account->account_key . '/' . $this->document_key . '/' . $this->name); } /** * @return bool */ public function isPDFEmbeddable() { return $this->type == 'jpeg' || $this->type == 'png' || $this->preview; } /** * @return \Illuminate\Contracts\Routing\UrlGenerator|null|string */ public function getVFSJSUrl() { if (! $this->isPDFEmbeddable()) { return null; } return url('documents/js/'.$this->public_id.'/'.$this->name.'.js'); } /** * @return \Illuminate\Contracts\Routing\UrlGenerator|null|string */ public function getClientVFSJSUrl() { if (! $this->isPDFEmbeddable()) { return null; } return url('client/documents/js/'.$this->public_id.'/'.$this->name.'.js'); } /** * @return \Illuminate\Contracts\Routing\UrlGenerator|null|string */ public function getPreviewUrl() { return $this->preview ? url('documents/preview/'.$this->public_id.'/'.$this->name.'.'.pathinfo($this->preview, PATHINFO_EXTENSION)) : null; } /** * @return array */ 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; } /** * @return mixed */ public function cloneDocument() { $document = self::createNew($this); $document->path = $this->path; $document->preview = $this->preview; $document->name = $this->name; $document->type = $this->type; $document->disk = $this->disk; $document->hash = $this->hash; $document->size = $this->size; $document->width = $this->width; $document->height = $this->height; return $document; } public function scopeProposalImages($query) { return $query->whereIsProposal(1); } } Document::deleted(function ($document) { $same_path_count = DB::table('documents') ->where('documents.account_id', '=', $document->account_id) ->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) ->where('documents.preview', '=', $document->preview) ->where('documents.disk', '=', $document->disk) ->count(); if (! $same_preview_count) { $document->getDisk()->delete($document->preview); } } });