refactor: replace custom CRM with Monica fork
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
Some checks failed
Build & Push Monica Image to Gitea Registry / build-and-push (push) Failing after 9s
This commit is contained in:
46
app/Services/Account/Photo/DestroyPhoto.php
Normal file
46
app/Services/Account/Photo/DestroyPhoto.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Account\Photo;
|
||||
|
||||
use App\Models\Account\Photo;
|
||||
use App\Services\BaseService;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class DestroyPhoto extends BaseService
|
||||
{
|
||||
/**
|
||||
* Get the validation rules that apply to the service.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'account_id' => 'required|integer|exists:accounts,id',
|
||||
'photo_id' => 'required|integer|exists:photos,id',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy a photo.
|
||||
*
|
||||
* @param array $data
|
||||
* @return bool
|
||||
*/
|
||||
public function execute(array $data): bool
|
||||
{
|
||||
$this->validate($data);
|
||||
|
||||
$photo = Photo::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['photo_id']);
|
||||
|
||||
// Delete the physical photo
|
||||
// Throws FileNotFoundException
|
||||
Storage::delete($photo->new_filename);
|
||||
|
||||
// Delete the object in the DB
|
||||
$photo->delete();
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
219
app/Services/Account/Photo/UploadPhoto.php
Normal file
219
app/Services/Account/Photo/UploadPhoto.php
Normal file
@@ -0,0 +1,219 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services\Account\Photo;
|
||||
|
||||
use Illuminate\Support\Arr;
|
||||
use Illuminate\Support\Str;
|
||||
use App\Models\Account\Photo;
|
||||
use App\Services\BaseService;
|
||||
use function Safe\finfo_open;
|
||||
use function Safe\preg_match;
|
||||
use App\Helpers\StorageHelper;
|
||||
use App\Models\Contact\Contact;
|
||||
use function Safe\base64_decode;
|
||||
use Intervention\Image\Facades\Image;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Intervention\Image\Exception\NotReadableException;
|
||||
|
||||
class UploadPhoto extends BaseService
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
Validator::extend('photo', function ($attribute, $value, $parameters, $validator) {
|
||||
return $this->isValidPhoto($value);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the service.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function rules()
|
||||
{
|
||||
return [
|
||||
'account_id' => 'required|integer|exists:accounts,id',
|
||||
'contact_id' => 'required|integer|exists:contacts,id',
|
||||
'photo' => 'required_without:data|file|image',
|
||||
'data' => 'required_without:photo|string|photo',
|
||||
'extension' => 'nullable|string',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a photo.
|
||||
*
|
||||
* @param array $data
|
||||
* @return Photo|null
|
||||
*/
|
||||
public function execute(array $data): ?Photo
|
||||
{
|
||||
$this->validate($data);
|
||||
|
||||
$contact = Contact::where('account_id', $data['account_id'])
|
||||
->findOrFail($data['contact_id']);
|
||||
|
||||
$contact->throwInactive();
|
||||
|
||||
$array = null;
|
||||
if (Arr::has($data, 'photo')) {
|
||||
$array = $this->importPhoto($data);
|
||||
} else {
|
||||
$array = $this->importFile($data);
|
||||
}
|
||||
|
||||
if (! $array) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return tap(Photo::create($array), function ($photo) use ($contact): void {
|
||||
$contact->photos()->syncWithoutDetaching([$photo->id]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an array with the necessary fields to create the photo object.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
private function importPhoto($data): array
|
||||
{
|
||||
$photo = $data['photo'];
|
||||
|
||||
return [
|
||||
'account_id' => $data['account_id'],
|
||||
'original_filename' => $photo->getClientOriginalName(),
|
||||
'filesize' => $photo->getSize(),
|
||||
'mime_type' => (new \Mimey\MimeTypes)->getMimeType($photo->guessClientExtension()),
|
||||
'new_filename' => $photo->store('photos', [
|
||||
'disk' => config('filesystems.default'),
|
||||
'visibility' => config('filesystems.default_visibility'),
|
||||
]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload the photo.
|
||||
*
|
||||
* @return array|null
|
||||
*/
|
||||
private function importFile(array $data): ?array
|
||||
{
|
||||
$filename = Str::random(40);
|
||||
|
||||
try {
|
||||
$image = Image::make($data['data']);
|
||||
} catch (NotReadableException $e) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tempfile = $this->storeImage('local', $image, 'temp/'.$filename);
|
||||
|
||||
try {
|
||||
$storagePath = StorageHelper::disk('local')->path($tempfile);
|
||||
// This sets the basePath to get the filesize later
|
||||
$image = $image->setFileInfoFromPath($storagePath);
|
||||
$extension = (new \Mimey\MimeTypes)->getExtension($image->mime());
|
||||
if (empty($extension)) {
|
||||
$extension = str_replace(' ', '', Arr::get($data, 'extension'));
|
||||
}
|
||||
if (! empty($extension)) {
|
||||
$filename .= '.'.$extension;
|
||||
}
|
||||
|
||||
$array = [
|
||||
'account_id' => $data['account_id'],
|
||||
'original_filename' => $filename,
|
||||
'filesize' => $image->filesize(),
|
||||
'mime_type' => $image->mime(),
|
||||
];
|
||||
|
||||
$array['new_filename'] = $this->storeImage(config('filesystems.default'), $image, 'photos/'.$filename);
|
||||
} finally {
|
||||
$storage = Storage::disk('local');
|
||||
if ($storage->exists($tempfile)) {
|
||||
$storage->delete($tempfile);
|
||||
}
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store the decoded image in the temp file.
|
||||
*
|
||||
* @param string $disk
|
||||
* @param \Intervention\Image\Image $image
|
||||
* @param string $filename
|
||||
* @return string|null
|
||||
*/
|
||||
private function storeImage(string $disk, $image, string $filename): ?string
|
||||
{
|
||||
$result = Storage::disk($disk)
|
||||
->put($path = $filename, (string) $image->stream(), config('filesystems.default_visibility'));
|
||||
|
||||
return $result ? $path : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if the source photo is a valid encoded photo.
|
||||
*
|
||||
* @param string $data
|
||||
* @return bool
|
||||
*/
|
||||
private function isValidPhoto(string $data): bool
|
||||
{
|
||||
return $this->isBinary($data) || $this->isDataUrl($data) || $this->isBase64($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if source data is binary data.
|
||||
*
|
||||
* @param string $data
|
||||
* @return bool
|
||||
*/
|
||||
private function isBinary(string $data): bool
|
||||
{
|
||||
$mime = finfo_buffer(finfo_open(FILEINFO_MIME_TYPE), $data); // @phpstan-ignore-line
|
||||
|
||||
return substr($mime, 0, 4) != 'text' && $mime != 'application/x-empty';
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if source data is data-url format.
|
||||
*
|
||||
* @param string $data
|
||||
* @return bool
|
||||
*/
|
||||
private function isDataUrl(string $data): bool
|
||||
{
|
||||
if (! is_string($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$pattern = "/^data:(?:image\/[a-zA-Z\-\.]+)(?:charset=\".+\")?;base64,(?P<data>.+)$/";
|
||||
preg_match($pattern, $data, $matches);
|
||||
|
||||
if (is_array($matches) && Arr::has($matches, 'data')) {
|
||||
return ! empty(base64_decode($matches['data']));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if source data is base64 encoded.
|
||||
*
|
||||
* @param string $data
|
||||
* @return bool
|
||||
*/
|
||||
private function isBase64(string $data): bool
|
||||
{
|
||||
if (! is_string($data)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return base64_encode(base64_decode($data)) === str_replace(["\n", "\r"], '', $data);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user