1
0
mirror of https://github.com/freescout-helpdesk/freescout.git synced 2024-11-25 11:52:29 +01:00
freescout/app/Attachment.php

269 lines
7.3 KiB
PHP
Raw Normal View History

2018-08-04 12:50:55 +02:00
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Storage;
class Attachment extends Model
{
const TYPE_TEXT = 0;
const TYPE_MULTIPART = 1;
const TYPE_MESSAGE = 2;
const TYPE_APPLICATION = 3;
const TYPE_AUDIO = 4;
const TYPE_IMAGE = 5;
const TYPE_VIDEO = 6;
const TYPE_MODEL = 7;
const TYPE_OTHER = 8;
2018-08-15 08:10:05 +02:00
const DIRECTORY = 'attachment';
2018-08-04 12:50:55 +02:00
// https://github.com/Webklex/laravel-imap/blob/master/src/IMAP/Attachment.php
public static $types = [
'message' => self::TYPE_MESSAGE,
'application' => self::TYPE_APPLICATION,
'audio' => self::TYPE_AUDIO,
'image' => self::TYPE_IMAGE,
'video' => self::TYPE_VIDEO,
'model' => self::TYPE_MODEL,
'text' => self::TYPE_TEXT,
'multipart' => self::TYPE_MULTIPART,
'other' => self::TYPE_OTHER,
];
2018-08-15 08:10:05 +02:00
/**
* Files with such extensions are being renamed on upload.
*/
public static $restricted_extensions = [
'php.*',
'sh',
'pl',
];
2018-08-04 12:50:55 +02:00
public $timestamps = false;
/**
* Get thread.
*/
public function thread()
{
return $this->belongsTo('App\Thread');
}
/**
* Save attachment to file and database.
*/
2018-08-15 08:10:05 +02:00
public static function create($file_name, $mime_type, $type, $content, $uploaded_file, $embedded = false, $thread_id = null, $user_id = null)
2018-08-04 12:50:55 +02:00
{
2018-08-15 08:10:05 +02:00
if (!$content && !$uploaded_file) {
2018-08-04 12:50:55 +02:00
return false;
}
// Check extension.
2018-08-15 08:10:05 +02:00
$extension = strtolower(pathinfo($file_name, PATHINFO_EXTENSION));
if (preg_match('/('.implode('|', self::$restricted_extensions).')/', $extension)) {
// Add underscore to the extension if file has restricted extension.
2018-08-15 08:10:05 +02:00
$file_name = $file_name.'_';
}
// Replace some symbols in file name.
// Slugify file names to ensure no bad browser interactions
$file_name = strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $file_name), '-'));
2018-08-15 08:10:05 +02:00
if (!$type) {
$type = self::detectType($mime_type);
}
2018-08-04 13:18:16 +02:00
$attachment = new self();
2018-08-04 12:50:55 +02:00
$attachment->thread_id = $thread_id;
2018-11-12 10:12:57 +01:00
$attachment->user_id = $user_id;
2018-08-04 12:50:55 +02:00
$attachment->file_name = $file_name;
$attachment->mime_type = $mime_type;
2018-11-12 10:12:57 +01:00
$attachment->type = $type;
$attachment->embedded = $embedded;
2018-08-04 12:50:55 +02:00
$attachment->save();
2018-08-15 08:10:05 +02:00
// Save file from content or copy file.
// We have to keep file name as is, so if file exists we create extra folder.
// Examples: 1/2/3
$file_dir = self::generatePath($attachment->id);
2018-08-04 12:50:55 +02:00
2018-08-15 08:10:05 +02:00
$i = 0;
do {
$i++;
$file_path = self::DIRECTORY.DIRECTORY_SEPARATOR.$file_dir.$i.DIRECTORY_SEPARATOR.$file_name;
} while (Storage::exists($file_path));
$file_dir .= $i.DIRECTORY_SEPARATOR;
if ($uploaded_file) {
$uploaded_file->storeAs(self::DIRECTORY.DIRECTORY_SEPARATOR.$file_dir, $file_name);
} else {
Storage::put($file_path, $content);
2018-08-04 12:50:55 +02:00
}
2018-08-04 13:18:16 +02:00
2018-08-15 08:10:05 +02:00
$attachment->file_dir = $file_dir;
$attachment->size = Storage::size($file_path);
$attachment->save();
return $attachment;
2018-08-04 12:50:55 +02:00
}
/**
2018-08-15 08:10:05 +02:00
* Get file path.
2018-11-12 10:12:57 +01:00
* Examples: 1/2, 1/3.
2018-08-04 13:18:16 +02:00
*
* @param int $id
*
2018-08-04 12:50:55 +02:00
* @return string
*/
2018-08-15 08:10:05 +02:00
public static function generatePath($id)
2018-08-04 12:50:55 +02:00
{
$hash = md5($id);
2018-08-04 13:18:16 +02:00
$first = -1;
2018-08-04 12:50:55 +02:00
$second = 0;
for ($i = 0; $i < strlen($hash); $i++) {
if (is_numeric($hash[$i])) {
if ($first == -1) {
$first = $hash[$i];
} else {
$second = $hash[$i];
break;
}
}
}
if ($first == -1) {
$first = 0;
}
2018-08-04 13:18:16 +02:00
2018-08-04 12:50:55 +02:00
return $first.DIRECTORY_SEPARATOR.$second.DIRECTORY_SEPARATOR;
}
2018-08-15 08:10:05 +02:00
/**
* Detect attachment type by it's mime type.
2018-11-12 10:12:57 +01:00
*
* @param string $mime_type
*
2018-08-15 08:10:05 +02:00
* @return int
*/
public static function detectType($mime_type)
{
if (preg_match("/^text\//", $mime_type)) {
return self::TYPE_TEXT;
} elseif (preg_match("/^message\//", $mime_type)) {
return self::TYPE_MESSAGE;
} elseif (preg_match("/^application\//", $mime_type)) {
return self::TYPE_APPLICATION;
} elseif (preg_match("/^audio\//", $mime_type)) {
return self::TYPE_AUDIO;
} elseif (preg_match("/^image\//", $mime_type)) {
return self::TYPE_IMAGE;
} elseif (preg_match("/^video\//", $mime_type)) {
return self::TYPE_VIDEO;
} elseif (preg_match("/^model\//", $mime_type)) {
return self::TYPE_MODEL;
} else {
return self::TYPE_OTHER;
}
}
2018-08-04 12:50:55 +02:00
/**
* Conver type name to integer.
*/
public static function typeNameToInt($type_name)
{
if (!empty(self::$types[$type_name])) {
return self::$types[$type_name];
} else {
return self::TYPE_OTHER;
}
}
/**
* Get attachment full public URL.
2018-08-04 13:18:16 +02:00
*
2018-08-04 12:50:55 +02:00
* @return string
*/
2018-08-08 09:52:53 +02:00
public function url()
2018-08-04 12:50:55 +02:00
{
2018-08-15 08:10:05 +02:00
return Storage::url($this->getStorageFilePath());
2018-08-04 12:50:55 +02:00
}
/**
* Convert size into human readable format.
2018-08-04 13:18:16 +02:00
*
2018-08-04 12:50:55 +02:00
* @return string
*/
public function getSizeName()
{
return self::formatBytes($this->size);
}
2018-08-15 08:10:05 +02:00
public function getStorageFilePath()
{
return self::DIRECTORY.DIRECTORY_SEPARATOR.$this->file_dir.$this->file_name;
}
public function getLocalFilePath()
{
return Storage::path(self::DIRECTORY.DIRECTORY_SEPARATOR.$this->file_dir.$this->file_name);
}
2018-08-04 12:50:55 +02:00
public static function formatBytes($size, $precision = 0)
{
$size = (int) $size;
if ($size > 0) {
$base = log($size) / log(1024);
2018-08-04 13:18:16 +02:00
$suffixes = [' b', ' KB', ' MB', ' GB', ' TB'];
2018-08-04 12:50:55 +02:00
2018-08-04 13:18:16 +02:00
return round(pow(1024, $base - floor($base)), $precision).$suffixes[floor($base)];
2018-08-04 12:50:55 +02:00
} else {
return $size;
}
}
2018-08-15 08:10:05 +02:00
/**
* Delete attachments from disk and DB.
* Embeds are not taken into account.
2018-11-12 10:12:57 +01:00
*
* @param array $attachments
2018-08-15 08:10:05 +02:00
*/
public static function deleteByIds($attachment_ids)
{
2018-09-14 10:24:04 +02:00
if (!count($attachment_ids)) {
return;
}
2018-11-12 10:12:57 +01:00
$attachments = self::whereIn('id', $attachment_ids)->get();
2018-08-15 08:10:05 +02:00
// Delete from disk
foreach ($attachments as $attachment) {
Storage::delete($attachment->getStorageFilePath());
}
// Delete from DB
2018-11-12 10:12:57 +01:00
self::whereIn('id', $attachment_ids)->delete();
2018-08-15 08:10:05 +02:00
}
2019-07-12 15:56:07 +02:00
/**
* Generate dummy ID for attachment.
* Not used for now.
*/
/*public static function genrateDummyId()
{
if (!$this->id) {
// Math.random should be unique because of its seeding algorithm.
// Convert it to base 36 (numbers + letters), and grab the first 9 characters
// after the decimal.
$hash = mt_rand() / mt_getrandmax();
$hash = base_convert($hash, 10, 36);
$id = substr($hash, 2, 9);
} else {
$id = substr(md5($this->id), 0, 9);
}
return $id;
}*/
2018-08-04 12:50:55 +02:00
}