kopia lustrzana https://github.com/nextcloud/social
257 wiersze
7.8 KiB
PHP
257 wiersze
7.8 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
// SPDX-FileCopyrightText: Carl Schwan <carl@carlschwan.eu>
|
|
// SPDX-License-Identifier: AGPL-3.0-or-later
|
|
|
|
namespace OCA\Social\Controller;
|
|
|
|
use OCA\Social\Entity\MediaAttachment;
|
|
use OCA\Social\Service\AccountFinder;
|
|
use OCP\AppFramework\Http;
|
|
use OCP\AppFramework\Http\Response;
|
|
use OCP\AppFramework\Http\DataResponse;
|
|
use OCP\AppFramework\Http\DataDownloadResponse;
|
|
use OCP\AppFramework\Http\NotFoundResponse;
|
|
use OCP\DB\ORM\IEntityManager;
|
|
use OCP\Files\IAppData;
|
|
use OCP\Files\NotFoundException;
|
|
use OCP\IL10N;
|
|
use OCP\AppFramework\Controller;
|
|
use OCP\Files\IMimeTypeDetector;
|
|
use OCP\Image;
|
|
use OCP\IRequest;
|
|
use OCP\IURLGenerator;
|
|
use OCP\IUserSession;
|
|
use OCP\Util;
|
|
use Psr\Log\LoggerInterface;
|
|
|
|
class MediaApiController extends Controller {
|
|
|
|
private IL10N $l10n;
|
|
private IMimeTypeDetector $mimeTypeDetector;
|
|
private IAppData $appData;
|
|
private IUserSession $userSession;
|
|
private AccountFinder $accountFinder;
|
|
private IEntityManager $entityManager;
|
|
private IURLGenerator $generator;
|
|
|
|
public const IMAGE_MIME_TYPES = [
|
|
'image/png',
|
|
'image/jpeg',
|
|
'image/jpg',
|
|
'image/gif',
|
|
'image/x-xbitmap',
|
|
'image/x-ms-bmp',
|
|
'image/bmp',
|
|
'image/svg+xml',
|
|
'image/webp',
|
|
];
|
|
|
|
public function __construct(
|
|
string $appName,
|
|
IRequest $request,
|
|
IL10N $l10n,
|
|
IMimeTypeDetector $mimeTypeDetector,
|
|
IAppData $appData,
|
|
IUserSession $userSession,
|
|
AccountFinder $accountFinder,
|
|
IEntityManager $entityManager,
|
|
IURLGenerator $generator,
|
|
LoggerInterface $logger
|
|
) {
|
|
parent::__construct($appName, $request);
|
|
$this->l10n = $l10n;
|
|
$this->mimeTypeDetector = $mimeTypeDetector;
|
|
$this->appData = $appData;
|
|
$this->userSession = $userSession;
|
|
$this->accountFinder = $accountFinder;
|
|
$this->entityManager = $entityManager;
|
|
$this->generator = $generator;
|
|
$this->logger = $logger;
|
|
}
|
|
|
|
/**
|
|
* Creates an attachment to be used with a new status.
|
|
*
|
|
* @NoAdminRequired
|
|
*/
|
|
public function uploadMedia(?string $description, ?string $focus = ''): DataResponse {
|
|
try {
|
|
$file = $this->getUploadedFile('file');
|
|
if (!isset($file['tmp_name'], $file['name'], $file['type'])) {
|
|
return new DataResponse(['error' => 'No uploaded file'], Http::STATUS_BAD_REQUEST);
|
|
}
|
|
|
|
if (!in_array($file['type'], MediaAttachment::IMAGE_MIME_TYPES, true)) {
|
|
return new DataResponse(['error' => 'Image type not supported'], Http::STATUS_BAD_REQUEST);
|
|
}
|
|
|
|
$account = $this->accountFinder->getCurrentAccount($this->userSession->getUser());
|
|
|
|
$meta = [];
|
|
$this->processFocus($focus, $meta);
|
|
|
|
$newFileResource = fopen($file['tmp_name'], 'rb');
|
|
if (!is_resource($newFileResource)) {
|
|
return new DataResponse(['error' => 'Image type not supported'], Http::STATUS_BAD_REQUEST);
|
|
}
|
|
|
|
$image = new Image();
|
|
$image->loadFromFileHandle($newFileResource);
|
|
$meta['original'] = [
|
|
"width" => $image->width(),
|
|
"height" => $image->height(),
|
|
"size" => $image->width() . "x" . $image->height(),
|
|
"aspect" => $image->width() / $image->height(),
|
|
];
|
|
|
|
$attachment = MediaAttachment::create();
|
|
$attachment->setMimetype($file['type']);
|
|
$attachment->setAccount($account);
|
|
$attachment->setDescription($description ?? '');
|
|
$attachment->setMeta($meta);
|
|
$this->entityManager->persist($attachment);
|
|
$this->entityManager->flush();
|
|
|
|
try {
|
|
$folder = $this->appData->getFolder('media-attachments');
|
|
} catch (NotFoundException $e) {
|
|
$folder = $this->appData->newFolder('media-attachments');
|
|
}
|
|
assert($attachment->getId() !== '');
|
|
$folder->newFile($attachment->getId(), $image->data());
|
|
|
|
return new DataResponse($attachment->toMastodonApi($this->generator));
|
|
} catch (\Exception $e) {
|
|
$this->logger->error($e->getMessage(), ['exception' => $e]);
|
|
return new DataResponse([
|
|
"error" => "Validation failed: File content type is invalid, File is invalid",
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
*/
|
|
public function updateMedia(string $id, ?string $description, ?string $focus = ''): Response {
|
|
try {
|
|
$account = $this->accountFinder->getCurrentAccount($this->userSession->getUser());
|
|
$attachementRepository = $this->entityManager->getRepository(MediaAttachment::class);
|
|
$attachement = $attachementRepository->findOneBy([
|
|
'id' => $id,
|
|
]);
|
|
if ($attachement->getAccount()->getId() !== $account->getId()) {
|
|
throw new NotFoundResponse();
|
|
}
|
|
|
|
$attachement->setDescription($description ?? '');
|
|
$this->entityManager->persist($attachement);
|
|
$this->entityManager->flush();
|
|
|
|
return new DataResponse($attachement->toMastodonApi($this->generator));
|
|
} catch (\Exception $e) {
|
|
$this->logger->error($e->getMessage(), ['exception' => $e]);
|
|
return new DataResponse([
|
|
"error" => "Validation failed: File content type is invalid, File is invalid",
|
|
], 500);
|
|
}
|
|
}
|
|
|
|
private function processFocus(string $focus, array &$meta): void {
|
|
if ($focus === '') {
|
|
return;
|
|
}
|
|
|
|
try {
|
|
[$x, $y] = explode(',', $focus);
|
|
$meta['focus'] = ['x' => $x, 'y' => $y];
|
|
} catch (\Exception $e) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
private function getUploadedFile(string $key): array {
|
|
$file = $this->request->getUploadedFile($key);
|
|
$error = null;
|
|
$phpFileUploadErrors = [
|
|
UPLOAD_ERR_OK => $this->l10n->t('The file was uploaded'),
|
|
UPLOAD_ERR_INI_SIZE => $this->l10n->t('The uploaded file exceeds the upload_max_filesize directive in php.ini'),
|
|
UPLOAD_ERR_FORM_SIZE => $this->l10n->t('The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form'),
|
|
UPLOAD_ERR_PARTIAL => $this->l10n->t('The file was only partially uploaded'),
|
|
UPLOAD_ERR_NO_FILE => $this->l10n->t('No file was uploaded'),
|
|
UPLOAD_ERR_NO_TMP_DIR => $this->l10n->t('Missing a temporary folder'),
|
|
UPLOAD_ERR_CANT_WRITE => $this->l10n->t('Could not write file to disk'),
|
|
UPLOAD_ERR_EXTENSION => $this->l10n->t('A PHP extension stopped the file upload'),
|
|
];
|
|
|
|
if (empty($file)) {
|
|
$error = $this->l10n->t('No file uploaded or file size exceeds maximum of %s', [Util::humanFileSize(Util::uploadLimit())]);
|
|
}
|
|
if (!empty($file) && array_key_exists('error', $file) && $file['error'] !== UPLOAD_ERR_OK) {
|
|
$error = $phpFileUploadErrors[$file['error']];
|
|
}
|
|
if ($error !== null) {
|
|
throw new \Exception($error);
|
|
}
|
|
return $file;
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
* @NoCSRFRequired
|
|
*/
|
|
public function getMedia(string $shortcode, string $extension): DataDownloadResponse {
|
|
try {
|
|
$folder = $this->appData->getFolder('media-attachments');
|
|
} catch (NotFoundException $e) {
|
|
$folder = $this->appData->newFolder('media-attachments');
|
|
}
|
|
$attachementRepository = $this->entityManager->getRepository(MediaAttachment::class);
|
|
$attachement = $attachementRepository->findOneBy([
|
|
'shortcode' => $shortcode,
|
|
]);
|
|
$file = $folder->getFile($attachement->getId());
|
|
return new DataDownloadResponse(
|
|
$file->getContent(),
|
|
(string) Http::STATUS_OK,
|
|
$this->getSecureMimeType($file->getMimeType())
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @NoAdminRequired
|
|
*/
|
|
public function deleteMedia(string $id): DataResponse {
|
|
try {
|
|
$folder = $this->appData->getFolder('media-attachments');
|
|
} catch (NotFoundException $e) {
|
|
$folder = $this->appData->newFolder('media-attachments');
|
|
}
|
|
$attachementRepository = $this->entityManager->getRepository(MediaAttachment::class);
|
|
$attachement = $attachementRepository->findOneBy([
|
|
'id' => $id,
|
|
]);
|
|
$file = $folder->getFile($attachement->getId());
|
|
$file->delete();
|
|
$this->entityManager->remove($attachement);
|
|
$this->entityManager->flush();
|
|
return new DataResponse(['removed']);
|
|
}
|
|
|
|
/**
|
|
* Allow all supported mimetypes
|
|
* Use mimetype detector for the other ones
|
|
*
|
|
* @param string $mimetype
|
|
* @return string
|
|
*/
|
|
private function getSecureMimeType(string $mimetype): string {
|
|
if (in_array($mimetype, self::IMAGE_MIME_TYPES)) {
|
|
return $mimetype;
|
|
}
|
|
return $this->mimeTypeDetector->getSecureMimeType($mimetype);
|
|
}
|
|
}
|