forked from GNUsocial/gnu-social
[Media] Support any kind of thumbnails in the Core
Sanitize Attachments instead of Validate (part 1) Ensure the intended filetypes and mimetypes during Vips conversions (part 1) Various bug fixes
This commit is contained in:
parent
143ecea376
commit
832a5c0bd9
@ -114,7 +114,7 @@ END;
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$processed_attachments = [];
|
$processed_attachments = [];
|
||||||
foreach ($attachments as $f) {
|
foreach ($attachments as $f) { // where $f is a Symfony\Component\HttpFoundation\File\UploadedFile
|
||||||
$processed_attachments[] = GSFile::validateAndStoreFileAsAttachment(
|
$processed_attachments[] = GSFile::validateAndStoreFileAsAttachment(
|
||||||
$f, Common::config('attachments', 'dir'),
|
$f, Common::config('attachments', 'dir'),
|
||||||
Security::sanitize($f->getClientOriginalName()),
|
Security::sanitize($f->getClientOriginalName()),
|
||||||
|
@ -25,6 +25,7 @@ use App\Core\Controller;
|
|||||||
use App\Core\DB\DB;
|
use App\Core\DB\DB;
|
||||||
use App\Core\Event;
|
use App\Core\Event;
|
||||||
use App\Core\GSFile;
|
use App\Core\GSFile;
|
||||||
|
use function App\Core\I18n\_m;
|
||||||
use App\Core\Router\Router;
|
use App\Core\Router\Router;
|
||||||
use App\Entity\AttachmentThumbnail;
|
use App\Entity\AttachmentThumbnail;
|
||||||
use App\Util\Common;
|
use App\Util\Common;
|
||||||
@ -34,7 +35,7 @@ use App\Util\Exception\ServerException;
|
|||||||
use Symfony\Component\HttpFoundation\HeaderUtils;
|
use Symfony\Component\HttpFoundation\HeaderUtils;
|
||||||
use Symfony\Component\HttpFoundation\Request;
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use function App\Core\I18n\_m;
|
use Symfony\Component\Mime\MimeTypes;
|
||||||
|
|
||||||
class Attachment extends Controller
|
class Attachment extends Controller
|
||||||
{
|
{
|
||||||
@ -77,32 +78,31 @@ class Attachment extends Controller
|
|||||||
*/
|
*/
|
||||||
public function attachment_view(Request $request, int $id)
|
public function attachment_view(Request $request, int $id)
|
||||||
{
|
{
|
||||||
return $this->attachment($id, fn (array $res) => GSFile::sendFile($res['filepath'], $res['mimetype'], $res['title'], HeaderUtils::DISPOSITION_INLINE));
|
return $this->attachment($id, fn (array $res) => GSFile::sendFile($res['filepath'], $res['mimetype'], GSFile::titleToFilename($res['title'], $res['mimetype']), HeaderUtils::DISPOSITION_INLINE));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function attachment_download(Request $request, int $id)
|
public function attachment_download(Request $request, int $id)
|
||||||
{
|
{
|
||||||
return $this->attachment($id, fn (array $res) => GSFile::sendFile($res['filepath'], $res['mimetype'], $res['title'], HeaderUtils::DISPOSITION_ATTACHMENT));
|
return $this->attachment($id, fn (array $res) => GSFile::sendFile($res['filepath'], $res['mimetype'], GSFile::titleToFilename($res['title'], $res['mimetype']), HeaderUtils::DISPOSITION_ATTACHMENT));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Controller to produce a thumbnail for a given attachment id
|
* Controller to produce a thumbnail for a given attachment id
|
||||||
*
|
*
|
||||||
* @param Request $request
|
* @param Request $request
|
||||||
* @param int $id Attachment ID
|
* @param int $id Attachment ID
|
||||||
*
|
*
|
||||||
* @return Response
|
|
||||||
* @throws ClientException
|
* @throws ClientException
|
||||||
* @throws NotFoundException
|
* @throws NotFoundException
|
||||||
* @throws ServerException
|
* @throws ServerException
|
||||||
* @throws \App\Util\Exception\DuplicateFoundException
|
* @throws \App\Util\Exception\DuplicateFoundException
|
||||||
|
*
|
||||||
|
* @return Response
|
||||||
*/
|
*/
|
||||||
public function attachment_thumbnail(Request $request, int $id): Response
|
public function attachment_thumbnail(Request $request, int $id): Response
|
||||||
{
|
{
|
||||||
$attachment = DB::findOneBy('attachment', ['id' => $id]);
|
$attachment = DB::findOneBy('attachment', ['id' => $id]);
|
||||||
if (preg_match('/^(image|video)/', $attachment->getMimeType()) !== 1) {
|
|
||||||
throw new ClientException(_m('Can not generate thumbnail for attachment with id={id}', ['id' => $id]));
|
|
||||||
}
|
|
||||||
if (!is_null($attachment->getScope())) {
|
if (!is_null($attachment->getScope())) {
|
||||||
// && ($attachment->scope | VisibilityScope::PUBLIC) != 0
|
// && ($attachment->scope | VisibilityScope::PUBLIC) != 0
|
||||||
// $user = Common::ensureLoggedIn();
|
// $user = Common::ensureLoggedIn();
|
||||||
@ -125,7 +125,8 @@ class Attachment extends Controller
|
|||||||
|
|
||||||
$filename = $thumbnail->getFilename();
|
$filename = $thumbnail->getFilename();
|
||||||
$path = $thumbnail->getPath();
|
$path = $thumbnail->getPath();
|
||||||
|
$mimetype = $attachment->getMimetype();
|
||||||
|
|
||||||
return GSFile::sendFile(filepath: $path, mimetype: $attachment->getMimetype(), output_filename: $filename, disposition: 'inline');
|
return GSFile::sendFile(filepath: $path, mimetype: $mimetype, output_filename: $filename . '.' . MimeTypes::getDefault()->getExtensions($mimetype)[0], disposition: HeaderUtils::DISPOSITION_INLINE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
namespace App\Core;
|
namespace App\Core;
|
||||||
|
|
||||||
use App\Core\DB\DB;
|
use App\Core\DB\DB;
|
||||||
|
use function App\Core\I18n\_m;
|
||||||
use App\Entity\Attachment;
|
use App\Entity\Attachment;
|
||||||
use App\Util\Common;
|
use App\Util\Common;
|
||||||
use App\Util\Exception\ClientException;
|
use App\Util\Exception\ClientException;
|
||||||
@ -34,7 +35,7 @@ use SplFileInfo;
|
|||||||
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
use Symfony\Component\HttpFoundation\BinaryFileResponse;
|
||||||
use Symfony\Component\HttpFoundation\HeaderUtils;
|
use Symfony\Component\HttpFoundation\HeaderUtils;
|
||||||
use Symfony\Component\HttpFoundation\Response;
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
use function App\Core\I18n\_m;
|
use Symfony\Component\Mime\MimeTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GNU social's File Abstraction
|
* GNU social's File Abstraction
|
||||||
@ -53,13 +54,14 @@ class GSFile
|
|||||||
* Perform file validation (checks and normalization) and store the given file
|
* Perform file validation (checks and normalization) and store the given file
|
||||||
*
|
*
|
||||||
* @param SplFileInfo $file
|
* @param SplFileInfo $file
|
||||||
* @param string $dest_dir
|
* @param string $dest_dir
|
||||||
* @param null|string $title
|
* @param null|string $title
|
||||||
* @param bool $is_local
|
* @param bool $is_local
|
||||||
* @param null|int $actor_id
|
* @param null|int $actor_id
|
||||||
|
*
|
||||||
|
* @throws DuplicateFoundException
|
||||||
*
|
*
|
||||||
* @return Attachment
|
* @return Attachment
|
||||||
* @throws DuplicateFoundException
|
|
||||||
*/
|
*/
|
||||||
public static function validateAndStoreFileAsAttachment(SplFileInfo $file,
|
public static function validateAndStoreFileAsAttachment(SplFileInfo $file,
|
||||||
string $dest_dir,
|
string $dest_dir,
|
||||||
@ -75,18 +77,22 @@ class GSFile
|
|||||||
// The following properly gets the mimetype with `file` or other
|
// The following properly gets the mimetype with `file` or other
|
||||||
// available methods, so should be safe
|
// available methods, so should be safe
|
||||||
$mimetype = $file->getMimeType();
|
$mimetype = $file->getMimeType();
|
||||||
$title = $width = $height = null;
|
$width = $height = null;
|
||||||
Event::handle('AttachmentValidation', [&$file, &$mimetype, &$title, &$width, &$height]);
|
Event::handle('AttachmentSanitization', [&$file, &$mimetype, &$title, &$width, &$height]);
|
||||||
|
if ($is_local) {
|
||||||
|
$filesize = $file->getSize();
|
||||||
|
Event::handle('EnforceQuota', [$actor_id, $filesize]);
|
||||||
|
}
|
||||||
$attachment = Attachment::create([
|
$attachment = Attachment::create([
|
||||||
'file_hash' => $hash,
|
'file_hash' => $hash,
|
||||||
'gsactor_id' => $actor_id,
|
'gsactor_id' => $actor_id,
|
||||||
'mimetype' => $mimetype,
|
'mimetype' => $mimetype,
|
||||||
'title' => $title ?: _m('Untitled attachment'),
|
'title' => $title,
|
||||||
'filename' => $hash,
|
'filename' => $hash,
|
||||||
'is_local' => $is_local,
|
'is_local' => $is_local,
|
||||||
'size' => $file->getSize(),
|
'size' => $file->getSize(),
|
||||||
'width' => $width,
|
'width' => $width,
|
||||||
'height' => $height,
|
'height' => $height,
|
||||||
]);
|
]);
|
||||||
$file->move($dest_dir, $hash);
|
$file->move($dest_dir, $hash);
|
||||||
DB::persist($attachment);
|
DB::persist($attachment);
|
||||||
@ -105,18 +111,18 @@ class GSFile
|
|||||||
if (Common::isValidHttpUrl($url)) {
|
if (Common::isValidHttpUrl($url)) {
|
||||||
$head = HTTPClient::head($url);
|
$head = HTTPClient::head($url);
|
||||||
// This must come before getInfo given that Symfony HTTPClient is lazy (thus forcing curl exec)
|
// This must come before getInfo given that Symfony HTTPClient is lazy (thus forcing curl exec)
|
||||||
$headers = $head->getHeaders();
|
$headers = $head->getHeaders();
|
||||||
$url = $head->getInfo('url'); // The last effective url (after getHeaders so it follows redirects)
|
$url = $head->getInfo('url'); // The last effective url (after getHeaders so it follows redirects)
|
||||||
$url_hash = hash(Attachment::URLHASH_ALGO, $url);
|
$url_hash = hash(Attachment::URLHASH_ALGO, $url);
|
||||||
try {
|
try {
|
||||||
return DB::findOneBy('attachment', ['remote_url_hash' => $url_hash]);
|
return DB::findOneBy('attachment', ['remote_url_hash' => $url_hash]);
|
||||||
} catch (NotFoundException) {
|
} catch (NotFoundException) {
|
||||||
$headers = array_change_key_case($headers, CASE_LOWER);
|
$headers = array_change_key_case($headers, CASE_LOWER);
|
||||||
$attachment = Attachment::create([
|
$attachment = Attachment::create([
|
||||||
'remote_url' => $url,
|
'remote_url' => $url,
|
||||||
'remote_url_hash' => $url_hash,
|
'remote_url_hash' => $url_hash,
|
||||||
'mimetype' => $headers['content-type'][0],
|
'mimetype' => $headers['content-type'][0],
|
||||||
'is_local' => false,
|
'is_local' => false,
|
||||||
]);
|
]);
|
||||||
DB::persist($attachment);
|
DB::persist($attachment);
|
||||||
Event::handle('AttachmentStoreNew', [&$attachment]);
|
Event::handle('AttachmentStoreNew', [&$attachment]);
|
||||||
@ -140,14 +146,14 @@ class GSFile
|
|||||||
Response::HTTP_OK,
|
Response::HTTP_OK,
|
||||||
[
|
[
|
||||||
'Content-Description' => 'File Transfer',
|
'Content-Description' => 'File Transfer',
|
||||||
'Content-Type' => $mimetype,
|
'Content-Type' => $mimetype,
|
||||||
'Content-Disposition' => HeaderUtils::makeDisposition($disposition, $output_filename ?: _m('Untitled attachment'), _m('Untitled attachment')),
|
'Content-Disposition' => HeaderUtils::makeDisposition($disposition, $output_filename ?? _m('Untitled attachment') . '.' . MimeTypes::getDefault()->getExtensions($mimetype)[0]),
|
||||||
'Cache-Control' => 'public',
|
'Cache-Control' => 'public',
|
||||||
],
|
],
|
||||||
$public = true,
|
public: true,
|
||||||
$disposition = null,
|
// contentDisposition: $disposition,
|
||||||
$add_etag = true,
|
autoEtag: true,
|
||||||
$add_last_modified = true
|
autoLastModified: true
|
||||||
);
|
);
|
||||||
if (Common::config('site', 'x_static_delivery')) {
|
if (Common::config('site', 'x_static_delivery')) {
|
||||||
$response->trustXSendfileTypeHeader();
|
$response->trustXSendfileTypeHeader();
|
||||||
@ -205,7 +211,7 @@ class GSFile
|
|||||||
*/
|
*/
|
||||||
public static function getAttachmentFileInfo(int $id): array
|
public static function getAttachmentFileInfo(int $id): array
|
||||||
{
|
{
|
||||||
$res = self::getFileInfo($id);
|
$res = self::getFileInfo($id);
|
||||||
$res['filepath'] = Common::config('attachments', 'dir') . $res['file_hash'];
|
$res['filepath'] = Common::config('attachments', 'dir') . $res['file_hash'];
|
||||||
return $res;
|
return $res;
|
||||||
}
|
}
|
||||||
@ -239,4 +245,42 @@ class GSFile
|
|||||||
}
|
}
|
||||||
return trim($mimetype);
|
return trim($mimetype);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an attachment title and mimetype allows to generate the most appropriate filename.
|
||||||
|
*
|
||||||
|
* @param string $title
|
||||||
|
* @param string $mimetype
|
||||||
|
* @param null|string $ext
|
||||||
|
* @param bool $force
|
||||||
|
*
|
||||||
|
* @return null|string
|
||||||
|
*/
|
||||||
|
public static function titleToFilename(string $title, string $mimetype, ?string &$ext = null, bool $force = false): string | null
|
||||||
|
{
|
||||||
|
$valid_extensions = MimeTypes::getDefault()->getExtensions($mimetype);
|
||||||
|
|
||||||
|
// If title seems to be a filename with an extension
|
||||||
|
if (preg_match('/\.[a-z0-9]/i', $title) === 1) {
|
||||||
|
$title_without_extension = substr($title, 0, strrpos($title, '.'));
|
||||||
|
$original_extension = substr($title, strrpos($title, '.') + 1);
|
||||||
|
if (empty(MimeTypes::getDefault()->getMimeTypes($original_extension)) || !in_array($original_extension, $valid_extensions)) {
|
||||||
|
unset($title_without_extension, $original_extension);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($force) {
|
||||||
|
return ($title_without_extension ?? $title) . ".{$ext}";
|
||||||
|
} else {
|
||||||
|
if (isset($original_extension)) {
|
||||||
|
return $title;
|
||||||
|
} else {
|
||||||
|
if (!empty($valid_extensions)) {
|
||||||
|
return "{$title}.{$valid_extensions[0]}";
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,14 @@ use App\Core\DB\DB;
|
|||||||
use App\Core\Entity;
|
use App\Core\Entity;
|
||||||
use App\Core\Event;
|
use App\Core\Event;
|
||||||
use App\Core\GSFile;
|
use App\Core\GSFile;
|
||||||
|
use function App\Core\I18n\_m;
|
||||||
use App\Core\Log;
|
use App\Core\Log;
|
||||||
use App\Util\Common;
|
use App\Util\Common;
|
||||||
|
use App\Util\Exception\ClientException;
|
||||||
use App\Util\Exception\NotFoundException;
|
use App\Util\Exception\NotFoundException;
|
||||||
use App\Util\Exception\ServerException;
|
use App\Util\Exception\ServerException;
|
||||||
use App\Util\TemporaryFile;
|
|
||||||
use DateTimeInterface;
|
use DateTimeInterface;
|
||||||
|
use Symfony\Component\Mime\MimeTypes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entity for Attachment thumbnails
|
* Entity for Attachment thumbnails
|
||||||
@ -53,6 +55,7 @@ class AttachmentThumbnail extends Entity
|
|||||||
// {{{ Autocode
|
// {{{ Autocode
|
||||||
// @codeCoverageIgnoreStart
|
// @codeCoverageIgnoreStart
|
||||||
private int $attachment_id;
|
private int $attachment_id;
|
||||||
|
private ?string $mimetype;
|
||||||
private int $width;
|
private int $width;
|
||||||
private int $height;
|
private int $height;
|
||||||
private string $filename;
|
private string $filename;
|
||||||
@ -69,6 +72,17 @@ class AttachmentThumbnail extends Entity
|
|||||||
return $this->attachment_id;
|
return $this->attachment_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setMimetype(?string $mimetype): self
|
||||||
|
{
|
||||||
|
$this->mimetype = $mimetype;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMimetype(): ?string
|
||||||
|
{
|
||||||
|
return $this->mimetype;
|
||||||
|
}
|
||||||
|
|
||||||
public function setWidth(int $width): self
|
public function setWidth(int $width): self
|
||||||
{
|
{
|
||||||
$this->width = $width;
|
$this->width = $width;
|
||||||
@ -151,27 +165,37 @@ class AttachmentThumbnail extends Entity
|
|||||||
try {
|
try {
|
||||||
return Cache::get('thumb-' . $attachment->getId() . "-{$width}x{$height}",
|
return Cache::get('thumb-' . $attachment->getId() . "-{$width}x{$height}",
|
||||||
function () use ($crop, $attachment, $width, $height, &$predicted_width, &$predicted_height) {
|
function () use ($crop, $attachment, $width, $height, &$predicted_width, &$predicted_height) {
|
||||||
[$predicted_width, $predicted_height] = self::predictScalingValues($attachment->getWidth(),$attachment->getHeight(), $width, $height, $crop);
|
[$predicted_width, $predicted_height] = self::predictScalingValues($attachment->getWidth(), $attachment->getHeight(), $width, $height, $crop);
|
||||||
return DB::findOneBy('attachment_thumbnail', ['attachment_id' => $attachment->getId(), 'width' => $predicted_width, 'height' => $predicted_height]);
|
return DB::findOneBy('attachment_thumbnail', ['attachment_id' => $attachment->getId(), 'width' => $predicted_width, 'height' => $predicted_height]);
|
||||||
});
|
});
|
||||||
} catch (NotFoundException $e) {
|
} catch (NotFoundException $e) {
|
||||||
$ext = image_type_to_extension(IMAGETYPE_WEBP, include_dot: true);
|
$thumbnail = self::create(['attachment_id' => $attachment->getId()]);
|
||||||
$temp = new TemporaryFile(['prefix' => 'gs-thumbnail', 'suffix' => $ext]);
|
Event::handle('ResizerAvailable', [&$event_map]);
|
||||||
$thumbnail = self::create(['attachment_id' => $attachment->getId()]);
|
$mimetype = $attachment->getMimetype();
|
||||||
$event_map = ['image' => 'ResizeImagePath', 'video' => 'ResizeVideoPath'];
|
$major_mime = GSFile::mimetypeMajor($mimetype);
|
||||||
$major_mime = GSFile::mimetypeMajor($attachment->getMimetype());
|
|
||||||
if (in_array($major_mime, array_keys($event_map)) && !Event::handle($event_map[$major_mime], [$attachment->getPath(), $temp->getRealPath(), &$width, &$height, $crop, &$mimetype])) {
|
if (in_array($major_mime, array_keys($event_map))) {
|
||||||
$thumbnail->setWidth($predicted_width);
|
$temp = null; // Let the EncoderPlugin create a temporary file for us
|
||||||
$thumbnail->setHeight($predicted_height);
|
if (!Event::handle(
|
||||||
$filename = "{$predicted_width}x{$predicted_height}{$ext}-" . $attachment->getFileHash();
|
$event_map[$major_mime],
|
||||||
$temp->move(Common::config('thumbnail', 'dir'), $filename);
|
[$attachment->getPath(), &$temp, &$width, &$height, $crop, &$mimetype]
|
||||||
$thumbnail->setFilename($filename);
|
)
|
||||||
DB::persist($thumbnail);
|
) {
|
||||||
DB::flush();
|
$thumbnail->setWidth($predicted_width);
|
||||||
return $thumbnail;
|
$thumbnail->setHeight($predicted_height);
|
||||||
|
$ext = '.' . MimeTypes::getDefault()->getExtensions($temp->getMimeType())[0];
|
||||||
|
$filename = "{$predicted_width}x{$predicted_height}{$ext}-" . $attachment->getFileHash();
|
||||||
|
$thumbnail->setFilename($filename);
|
||||||
|
DB::persist($thumbnail);
|
||||||
|
DB::flush();
|
||||||
|
$temp->move(Common::config('thumbnail', 'dir'), $filename);
|
||||||
|
return $thumbnail;
|
||||||
|
} else {
|
||||||
|
Log::debug($m = ('Somehow the EncoderPlugin didn\'t handle ' . $attachment->getMimetype()));
|
||||||
|
throw new ServerException($m);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Log::debug($m = ('Cannot resize attachment with mimetype ' . $attachment->getMimetype()));
|
throw new ClientException(_m('Can not generate thumbnail for attachment with id={id}', ['id' => $attachment->getId()]));
|
||||||
throw new ServerException($m);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -278,10 +302,11 @@ class AttachmentThumbnail extends Entity
|
|||||||
'name' => 'attachment_thumbnail',
|
'name' => 'attachment_thumbnail',
|
||||||
'fields' => [
|
'fields' => [
|
||||||
'attachment_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Attachment.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'thumbnail for what attachment'],
|
'attachment_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Attachment.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'thumbnail for what attachment'],
|
||||||
'width' => ['type' => 'int', 'not null' => true, 'description' => 'width of thumbnail'],
|
// 'mimetype' => ['type' => 'varchar', 'length' => 50, 'description' => 'mime type of resource'],
|
||||||
'height' => ['type' => 'int', 'not null' => true, 'description' => 'height of thumbnail'],
|
'width' => ['type' => 'int', 'not null' => true, 'description' => 'width of thumbnail'],
|
||||||
'filename' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'thumbnail filename'],
|
'height' => ['type' => 'int', 'not null' => true, 'description' => 'height of thumbnail'],
|
||||||
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
'filename' => ['type' => 'varchar', 'length' => 191, 'not null' => true, 'description' => 'thumbnail filename'],
|
||||||
|
'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'],
|
||||||
],
|
],
|
||||||
'primary key' => ['attachment_id', 'width', 'height'],
|
'primary key' => ['attachment_id', 'width', 'height'],
|
||||||
'indexes' => [
|
'indexes' => [
|
||||||
|
@ -41,9 +41,9 @@ class TemporaryFile extends \SplFileInfo
|
|||||||
* @param array $options - ['prefix' => ?string, 'suffix' => ?string, 'mode' => ?string, 'directory' => ?string]
|
* @param array $options - ['prefix' => ?string, 'suffix' => ?string, 'mode' => ?string, 'directory' => ?string]
|
||||||
* Description of options:
|
* Description of options:
|
||||||
* > prefix: The file name will begin with that prefix, default is 'gs-php'
|
* > prefix: The file name will begin with that prefix, default is 'gs-php'
|
||||||
* > suffix: The file name will begin with that prefix, default is ''
|
* > suffix: The file name will end with that suffix, default is ''
|
||||||
* > mode: The file name will begin with that prefix, default is 'w+b'
|
* > mode: Operation mode, default is 'w+b'
|
||||||
* > directory: The file name will begin with that prefix, default is the system's temporary
|
* > directory: Directory where the file will be used, default is the system's temporary
|
||||||
*
|
*
|
||||||
* @throws TemporaryFileException
|
* @throws TemporaryFileException
|
||||||
*/
|
*/
|
||||||
@ -157,6 +157,23 @@ class TemporaryFile extends \SplFileInfo
|
|||||||
|
|
||||||
$destpath = rtrim($directory, '/\\') . DIRECTORY_SEPARATOR . $this->getName($filename);
|
$destpath = rtrim($directory, '/\\') . DIRECTORY_SEPARATOR . $this->getName($filename);
|
||||||
|
|
||||||
|
$this->commit($destpath, $dirmode, $filemode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release the hold on the temporary file and move it to the desired
|
||||||
|
* location, setting file permissions in the process.
|
||||||
|
*
|
||||||
|
* @param string $destpath Full path of destination file
|
||||||
|
* @param int $dirmode New directory permissions (in octal mode)
|
||||||
|
* @param int $filemode New file permissions (in octal mode)
|
||||||
|
*
|
||||||
|
* @throws TemporaryFileException
|
||||||
|
*
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
public function commit(string $destpath, int $dirmode = 0755, int $filemode = 0644): void
|
||||||
|
{
|
||||||
$temppath = $this->getRealPath();
|
$temppath = $this->getRealPath();
|
||||||
|
|
||||||
// Might be attempted, and won't end well
|
// Might be attempted, and won't end well
|
||||||
|
Loading…
Reference in New Issue
Block a user