From 969b4f991ece3962ed2c7f3753ebc010fecb4749 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sun, 30 Oct 2022 13:36:08 -0400 Subject: [PATCH] Create Profile\Attachment\Upload module class - Add missing response type in AjaxUpload initializition --- src/Module/Profile/Attachment/Upload.php | 169 +++++++++++++++++++ static/routes.config.php | 19 ++- view/js/filebrowser.js | 3 +- view/theme/frio/js/filebrowser.js | 5 +- view/theme/smoothly/templates/jot-header.tpl | 2 +- 5 files changed, 185 insertions(+), 13 deletions(-) create mode 100644 src/Module/Profile/Attachment/Upload.php diff --git a/src/Module/Profile/Attachment/Upload.php b/src/Module/Profile/Attachment/Upload.php new file mode 100644 index 000000000..0624ea5a3 --- /dev/null +++ b/src/Module/Profile/Attachment/Upload.php @@ -0,0 +1,169 @@ +. + * + */ + +namespace Friendica\Module\Profile\Attachment; + +use Friendica\App; +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\L10n; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Database\Database; +use Friendica\Model\Attach; +use Friendica\Model\User; +use Friendica\Module\Response; +use Friendica\Navigation\SystemMessages; +use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Util\Profiler; +use Friendica\Util\Strings; +use Psr\Log\LoggerInterface; + +/** + * Asynchronous attachment upload module + * + * Only used as the target action of the AjaxUpload Javascript library + */ +class Upload extends \Friendica\BaseModule +{ + /** @var Database */ + private $database; + + /** @var IHandleUserSessions */ + private $userSession; + + /** @var IManageConfigValues */ + private $config; + + /** @var SystemMessages */ + private $systemMessages; + + /** @var bool */ + private $isJson; + + public function __construct(SystemMessages $systemMessages, IManageConfigValues $config, IHandleUserSessions $userSession, Database $database, L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, array $server, array $parameters = []) + { + parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters); + + $this->database = $database; + $this->userSession = $userSession; + $this->config = $config; + $this->systemMessages = $systemMessages; + } + + protected function post(array $request = []) + { + if ($this->isJson = !empty($request['response']) && $request['response'] == 'json') { + $this->response->setType(Response::TYPE_JSON, 'application/json'); + } + + $nick = $this->parameters['nickname']; + $owner = User::getOwnerDataByNick($nick); + if (!$owner) { + $this->logger->warning('owner is not a valid record:', ['owner' => $owner, 'nick' => $nick]); + return $this->return(401, $this->t('Invalid request.')); + } + + $can_post = false; + $contact_id = 0; + $page_owner_uid = $owner['uid']; + $community_page = $owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY; + + if ($this->userSession->getLocalUserId() && $this->userSession->getLocalUserId() == $page_owner_uid) { + $can_post = true; + } elseif ($community_page && !empty($this->userSession->getRemoteContactID($page_owner_uid))) { + $contact_id = $this->userSession->getRemoteContactID($page_owner_uid); + $can_post = $this->database->exists('contact', ['blocked' => false, 'pending' => false, 'id' => $contact_id, 'uid' => $page_owner_uid]); + } + + if (!$can_post) { + $this->logger->warning('User does not have required permissions', ['contact_id' => $contact_id, 'page_owner_uid' => $page_owner_uid]); + return $this->return(403, $this->t('Permission denied.'), true); + } + + if (empty($_FILES['userfile'])) { + $this->logger->warning('No file uploaded (empty userfile)'); + return $this->return(401, $this->t('Invalid request.'), true); + } + + $tempFileName = $_FILES['userfile']['tmp_name']; + $fileName = basename($_FILES['userfile']['name']); + $fileSize = intval($_FILES['userfile']['size']); + $maxFileSize = $this->config->get('system', 'maxfilesize'); + + /* + * Found html code written in text field of form, when trying to upload a + * file with filesize greater than upload_max_filesize. Cause is unknown. + * Then Filesize gets <= 0. + */ + if ($fileSize <= 0) { + @unlink($tempFileName); + $msg = $this->t('Sorry, maybe your upload is bigger than the PHP configuration allows') . '
' . $this->t('Or - did you try to upload an empty file?'); + $this->logger->warning($msg, ['fileSize' => $fileSize]); + return $this->return(401, $msg, true); + } + + if ($maxFileSize && $fileSize > $maxFileSize) { + @unlink($tempFileName); + $msg = $this->t('File exceeds size limit of %s', Strings::formatBytes($maxFileSize)); + $this->logger->warning($msg, ['fileSize' => $fileSize]); + return $this->return(401, $msg); + } + + $newid = Attach::storeFile($tempFileName, $page_owner_uid, $fileName, '<' . $owner['id'] . '>'); + + @unlink($tempFileName); + + if ($newid === false) { + $msg = $this->t('File upload failed.'); + $this->logger->warning($msg); + return $this->return(500, $msg); + } + + if ($this->isJson) { + $content = json_encode(['ok' => true, 'id' => $newid], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT); + } else { + $content = "\n\n" . '[attachment]' . $newid . '[/attachment]' . "\n"; + } + + return $this->response->addContent($content); + } + + /** + * @param int $httpCode + * @param string $message + * @param bool $systemMessage + * @return void + * @throws InternalServerErrorException + */ + private function return(int $httpCode, string $message, bool $systemMessage = false): void + { + $this->response->setStatus($httpCode, $message); + + if ($this->isJson) { + $this->response->addContent(json_encode(['error' => $message], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT)); + } else { + if ($systemMessage) { + $this->systemMessages->addNotice($message); + } + + $this->response->addContent($message); + } + } +} diff --git a/static/routes.config.php b/static/routes.config.php index b27a70c69..1b0f68026 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -31,15 +31,16 @@ use Friendica\App\Router as R; use Friendica\Module; $profileRoutes = [ - '' => [Module\Profile\Index::class, [R::GET]], - '/profile' => [Module\Profile\Profile::class, [R::GET]], - '/schedule' => [Module\Profile\Schedule::class, [R::GET, R::POST]], - '/contacts/common' => [Module\Profile\Common::class, [R::GET]], - '/contacts[/{type}]' => [Module\Profile\Contacts::class, [R::GET]], - '/status[/{category}[/{date1}[/{date2}]]]' => [Module\Profile\Status::class, [R::GET]], - '/media' => [Module\Profile\Media::class, [R::GET]], - '/unkmail' => [Module\Profile\UnkMail::class, [R::GET, R::POST]], - '/photos/upload' => [Module\Profile\Photos\Upload::class, [ R::POST]], + '' => [Module\Profile\Index::class, [R::GET]], + '/attachment/upload' => [Module\Profile\Attachment\Upload::class, [ R::POST]], + '/profile' => [Module\Profile\Profile::class, [R::GET]], + '/schedule' => [Module\Profile\Schedule::class, [R::GET, R::POST]], + '/contacts/common' => [Module\Profile\Common::class, [R::GET]], + '/contacts[/{type}]' => [Module\Profile\Contacts::class, [R::GET]], + '/status[/{category}[/{date1}[/{date2}]]]' => [Module\Profile\Status::class, [R::GET]], + '/media' => [Module\Profile\Media::class, [R::GET]], + '/unkmail' => [Module\Profile\UnkMail::class, [R::GET, R::POST]], + '/photos/upload' => [Module\Profile\Photos\Upload::class, [ R::POST]], ]; $apiRoutes = [ diff --git a/view/js/filebrowser.js b/view/js/filebrowser.js index 33d06e708..e5bd2730c 100644 --- a/view/js/filebrowser.js +++ b/view/js/filebrowser.js @@ -123,8 +123,9 @@ var FileBrowser = { if ($("#upload-file").length) var file_uploader = new window.AjaxUpload( 'upload-file', - { action: 'wall_attach/'+FileBrowser.nickname+'?response=json', + { action: 'profile/' + FileBrowser.nickname + '/attachment/upload?response=json', name: 'userfile', + responseType: 'json', onSubmit: function(file,ext) { $('#profile-rotator').show(); $(".error").addClass('hidden'); }, onComplete: function(file,response) { if (response['error']!= undefined) { diff --git a/view/theme/frio/js/filebrowser.js b/view/theme/frio/js/filebrowser.js index 3cdd03e1a..67d3b7923 100644 --- a/view/theme/frio/js/filebrowser.js +++ b/view/theme/frio/js/filebrowser.js @@ -203,8 +203,9 @@ var FileBrowser = { if ($("#upload-file").length) { //AjaxUpload for files var file_uploader = new window.AjaxUpload("upload-file", { - action: "wall_attach/" + FileBrowser.nickname + "?response=json", + action: "profile/" + FileBrowser.nickname + "/attachment/upload?response=json", name: "userfile", + responseType: "json", onSubmit: function (file, ext) { $(".fbrowser-content").hide(); $(".fbrowser .profile-rotator-wrapper").show(); @@ -214,7 +215,7 @@ var FileBrowser = { if (response["error"] != undefined) { $(".error span").html(response["error"]); $(".error").removeClass("hidden"); - $("#profile-rotator").hide(); + $(".fbrowser .profile-rotator-wrapper").hide(); $(".fbrowser-content").show(); return; } diff --git a/view/theme/smoothly/templates/jot-header.tpl b/view/theme/smoothly/templates/jot-header.tpl index 71b877179..cfd21035d 100644 --- a/view/theme/smoothly/templates/jot-header.tpl +++ b/view/theme/smoothly/templates/jot-header.tpl @@ -75,7 +75,7 @@ function enableOnUser(){ var file_uploader = new window.AjaxUpload( 'wall-file-upload', - { action: 'wall_attach/{{$nickname}}', + { action: 'profile/{{$nickname}}/attachment/upload', name: 'userfile', onSubmit: function(file,ext) { $('#profile-rotator').show(); }, onComplete: function(file,response) {