From acfc11c43606f9706b05169f77a13b5d29383c3b Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 26 Nov 2022 08:56:24 +0000 Subject: [PATCH 01/23] Issue 12133: Account data can now be updated via API --- .../Mastodon/Accounts/UpdateCredentials.php | 74 ++++++++++++++++++- 1 file changed, 71 insertions(+), 3 deletions(-) diff --git a/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php b/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php index a2e7f395a..6427a904a 100644 --- a/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php +++ b/src/Module/Api/Mastodon/Accounts/UpdateCredentials.php @@ -21,8 +21,12 @@ namespace Friendica\Module\Api\Mastodon\Accounts; -use Friendica\App\Router; use Friendica\Core\Logger; +use Friendica\DI; +use Friendica\Model\Contact; +use Friendica\Model\Photo; +use Friendica\Model\Profile; +use Friendica\Model\User; use Friendica\Module\BaseApi; /** @@ -35,8 +39,72 @@ class UpdateCredentials extends BaseApi self::checkAllowedScope(self::SCOPE_WRITE); $uid = self::getCurrentUserID(); - Logger::info('Patch data', ['data' => $request]); + $owner = User::getOwnerDataById($uid); - $this->response->unsupported(Router::PATCH, $request); + $request = $this->getRequest([ + 'bot' => ($owner['contact-type'] == Contact::TYPE_NEWS), + 'discoverable' => $owner['net-publish'], + 'display_name' => $owner['name'], + 'fields_attributes' => [], + 'locked' => $owner['manually-approve'], + 'note' => $owner['about'], + 'avatar' => [], + 'header' => [], + ], $request); + + $user = []; + $profile = []; + + if ($request['bot']) { + $user['account-type'] = Contact::TYPE_NEWS; + $user['page-flags'] = User::PAGE_FLAGS_SOAPBOX; + } elseif ($owner['contact-type'] == Contact::TYPE_NEWS) { + $user['account-type'] = Contact::TYPE_PERSON; + } else { + $user['account-type'] = $owner['contact-type']; + } + + $profile['net-publish'] = $request['discoverable']; + + if (!empty($request['display_name'])) { + $user['username'] = $request['display_name']; + } + + if ($user['account-type'] == Contact::TYPE_COMMUNITY) { + $user['page-flags'] = $request['locked'] ? User::PAGE_FLAGS_PRVGROUP : User::PAGE_FLAGS_COMMUNITY; + } elseif ($user['account-type'] == Contact::TYPE_PERSON) { + if ($request['locked']) { + $user['page-flags'] = User::PAGE_FLAGS_NORMAL; + } elseif ($owner['page-flags'] == User::PAGE_FLAGS_NORMAL) { + $user['page-flags'] = User::PAGE_FLAGS_SOAPBOX; + } + } + + if (!empty($request['note'])) { + $profile['about'] = $request['note']; + } + + Logger::debug('Patch data', ['data' => $request, 'files' => $_FILES]); + + Logger::info('Update profile and user', ['uid' => $uid, 'user' => $user, 'profile' => $profile]); + + if (!empty($request['avatar'])) { + Photo::uploadAvatar(1, $request['avatar']); + } + + if (!empty($request['header'])) { + Photo::uploadBanner(1, $request['header']); + } + + User::update($user, $uid); + Profile::update($profile, $uid); + + $cdata = Contact::getPublicAndUserContactID($owner['id'], $uid); + if (empty($cdata)) { + DI::mstdnError()->InternalError(); + } + + $account = DI::mstdnAccount()->createFromContactId($cdata['user'], $uid); + $this->response->exitWithJson($account->toArray()); } } From 08b9d11d460f76e115c73c9530a75cbc6c6ca9fa Mon Sep 17 00:00:00 2001 From: Michael Date: Sat, 26 Nov 2022 09:00:35 +0000 Subject: [PATCH 02/23] Updated documentation --- doc/API-Mastodon.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index ab5b5dbbb..63c487caf 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -47,6 +47,7 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - [`POST /api/v1/accounts/:id/unmute`](https://docs.joinmastodon.org/methods/accounts/) - [`GET /api/v1/accounts/relationships`](https://docs.joinmastodon.org/methods/accounts/) - [`GET /api/v1/accounts/search`](https://docs.joinmastodon.org/methods/accounts) +- [`PATCH /api/v1/accounts/update_credentials`](https://docs.joinmastodon.org/methods/accounts/#update_credentials) - [`GET /api/v1/accounts/verify_credentials`](https://docs.joinmastodon.org/methods/accounts) - [`POST /api/v1/apps`](https://docs.joinmastodon.org/methods/apps/) - [`GET /api/v1/apps/verify_credentials`](https://docs.joinmastodon.org/methods/apps/) @@ -138,7 +139,6 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en These emdpoints are planned to be implemented somewhere in the future. -- [`PATCH /api/v1/accounts/update_credentials`](https://docs.joinmastodon.org/methods/accounts/) - [`POST /api/v1/accounts/:id/remove_from_followers`](https://github.com/mastodon/mastodon/pull/16864) - [`GET /api/v1/accounts/familiar_followers`](https://github.com/mastodon/mastodon/pull/17700) - [`GET /api/v1/accounts/lookup`](https://github.com/mastodon/mastodon/pull/15740) From ed7b175491bd3f06588401209491fe661665379c Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 26 Nov 2022 15:21:46 -0500 Subject: [PATCH 03/23] Catch worker argument type errors - This marks the invalid worker queue item as completed instead of endlessly retrying it - Address https://github.com/friendica/friendica/issues/11992#issuecomment-1327029331 --- src/Core/Worker.php | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/Core/Worker.php b/src/Core/Worker.php index 96f3e7ae0..47da04b35 100644 --- a/src/Core/Worker.php +++ b/src/Core/Worker.php @@ -568,7 +568,15 @@ class Worker // Set the workerLogger as new default logger if ($method_call) { - call_user_func_array(sprintf('Friendica\Worker\%s::execute', $funcname), $argv); + try { + call_user_func_array(sprintf('Friendica\Worker\%s::execute', $funcname), $argv); + } catch (\TypeError $e) { + // No need to defer a worker queue entry if the arguments are invalid + Logger::notice('Wrong worker arguments', ['class' => $funcname, 'argv' => $argv, 'queue' => $queue, 'message' => $e->getMessage()]); + } catch (\Throwable $e) { + Logger::error('Uncaught exception in worker execution', ['class' => get_class($e), 'message' => $e->getMessage(), 'code' => $e->getCode(), 'file' => $e->getFile() . ':' . $e->getLine(), 'trace' => $e->getTraceAsString()]); + Worker::defer(); + } } else { $funcname($argv, count($argv)); } From 34418d790d3ad780a7d39f8225f22d87e817be17 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 26 Nov 2022 15:38:21 -0500 Subject: [PATCH 04/23] Check for existence of $contact.photo_menu.edit in frio:contact/entry.tpl - Address https://github.com/friendica/friendica/issues/11992#issuecomment-1327030221 --- view/theme/frio/templates/contact/entry.tpl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/view/theme/frio/templates/contact/entry.tpl b/view/theme/frio/templates/contact/entry.tpl index 5d29238e7..17436b570 100644 --- a/view/theme/frio/templates/contact/entry.tpl +++ b/view/theme/frio/templates/contact/entry.tpl @@ -6,7 +6,7 @@
From d0b16b2fc15ac2b3d5db65586a7a94fda8f7224c Mon Sep 17 00:00:00 2001 From: Philipp Date: Fri, 25 Nov 2022 23:43:07 +0100 Subject: [PATCH 05/23] Move mod/fbrowser to src\Modules\Attachment|Photos\Browser --- src/App/Arguments.php | 2 + src/App/Page.php | 1 + src/App/Router.php | 2 +- src/Model/Photo.php | 53 ++++ src/Module/Attach.php | 1 - src/Module/Profile/Attachment/Browser.php | 81 +++++++ src/Module/Profile/Photos/Browser.php | 105 ++++++++ static/routes.config.php | 26 +- view/global.css | 12 +- view/js/filebrowser.js | 113 +++++---- view/js/main.js | 8 +- view/templates/jot-header.tpl | 4 +- view/templates/{ => profile}/filebrowser.tpl | 6 +- view/theme/frio/css/style.css | 4 +- view/theme/frio/js/filebrowser.js | 229 +++++++++--------- view/theme/frio/js/modal.js | 8 +- view/theme/frio/templates/jot-header.tpl | 4 +- view/theme/frio/templates/js_strings.tpl | 1 + .../templates/{ => profile}/filebrowser.tpl | 6 +- view/theme/quattro/dark/style.css | 12 +- view/theme/quattro/green/style.css | 12 +- view/theme/quattro/lilac/style.css | 12 +- view/theme/quattro/quattro.less | 12 +- view/theme/vier/style.css | 2 +- 24 files changed, 483 insertions(+), 233 deletions(-) create mode 100644 src/Module/Profile/Attachment/Browser.php create mode 100644 src/Module/Profile/Photos/Browser.php rename view/templates/{ => profile}/filebrowser.tpl (80%) rename view/theme/frio/templates/{ => profile}/filebrowser.tpl (85%) diff --git a/src/App/Arguments.php b/src/App/Arguments.php index 6dfdcb560..5d30f9bd2 100644 --- a/src/App/Arguments.php +++ b/src/App/Arguments.php @@ -85,6 +85,8 @@ class Arguments /** * @return string The module name based on the arguments + * @deprecated 2022.12 - With the new (sub-)routes, it's no more trustworthy, use the ModuleClass instead + * @see Router::getModuleClass() */ public function getModuleName(): string { diff --git a/src/App/Page.php b/src/App/Page.php index 37141426c..afc94fbdf 100644 --- a/src/App/Page.php +++ b/src/App/Page.php @@ -241,6 +241,7 @@ class Page implements ArrayAccess * being first */ $this->page['htmlhead'] = Renderer::replaceMacros($tpl, [ + '$local_nickname' => $app->getLoggedInUserNickname(), '$local_user' => $localUID, '$generator' => 'Friendica' . ' ' . App::VERSION, '$delitem' => $l10n->t('Delete this item?'), diff --git a/src/App/Router.php b/src/App/Router.php index 4e5f29521..b6fd1098f 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -266,7 +266,7 @@ class Router * @throws HTTPException\MethodNotAllowedException If a rule matched but the method didn't * @throws HTTPException\NotFoundException If no rule matched */ - private function getModuleClass(): string + public function getModuleClass(): string { $cmd = $this->args->getCommand(); $cmd = '/' . ltrim($cmd, '/'); diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 7c620b0ad..a139ba715 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -173,6 +173,59 @@ class Photo return $photo; } + /** + * Returns all browsable albums for a given user + * + * @param int $uid The given user + * + * @return array An array of albums + * @throws \Exception + */ + public static function getBrowsableAlbumsForUser(int $uid): array + { + $photos = DBA::toArray( + DBA::p( + "SELECT DISTINCT(`album`) AS `albume` FROM `photo` WHERE `uid` = ? AND NOT `photo-type` IN (?, ?)", + $uid, + static::CONTACT_AVATAR, + static::CONTACT_BANNER + ) + ); + + return array_column($photos, 'album'); + } + + /** + * Returns browsable photos for a given user (optional and a given album) + * + * @param int $uid The given user id + * @param string|null $album (optional) The given album + * + * @return array All photos of the user/album + * @throws \Exception + */ + public static function getBrowsablePhotosForUser(int $uid, string $album = null): array + { + if (!empty($album)) { + $sqlExtra = sprintf("AND `album` = '%S' ", DBA::escape($album)); + $sqlExtra2 = ""; + } else { + $sqlExtra = ''; + $sqlExtra2 = ' ORDER BY created DESC LIMIT 0, 10'; + } + + return DBA::toArray( + DBA::p( + "SELECT `resource-id`, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`filename`) AS `filename`, ANY_VALUE(`type`) AS `type`, + min(`scale`) AS `hiq`, max(`scale`) AS `loq`, ANY_VALUE(`desc`) AS `desc`, ANY_VALUE(`created`) AS `created` + FROM `photo` WHERE `uid` = ? $sqlExtra AND NOT `photo-type` IN (?, ?) + GROUP BY `resource-id` $sqlExtra2", + $uid, + Photo::CONTACT_AVATAR, + Photo::CONTACT_BANNER + )); + } + /** * Check if photo with given conditions exists * diff --git a/src/Module/Attach.php b/src/Module/Attach.php index a73beb2b8..17b2d6e90 100644 --- a/src/Module/Attach.php +++ b/src/Module/Attach.php @@ -37,7 +37,6 @@ class Attach extends BaseModule */ protected function rawContent(array $request = []) { - $a = DI::app(); if (empty($this->parameters['item'])) { throw new \Friendica\Network\HTTPException\BadRequestException(); } diff --git a/src/Module/Profile/Attachment/Browser.php b/src/Module/Profile/Attachment/Browser.php new file mode 100644 index 000000000..09429bff8 --- /dev/null +++ b/src/Module/Profile/Attachment/Browser.php @@ -0,0 +1,81 @@ +session = $session; + $this->app = $app; + } + + protected function content(array $request = []): string + { + if (!$this->session->getLocalUserId()) { + $this->baseUrl->redirect(); + } + + // Needed to match the correct template in a module that uses a different theme than the user/site/default + $theme = Strings::sanitizeFilePathItem($request['theme'] ?? ''); + if ($theme && is_file("view/theme/$theme/config.php")) { + $this->app->setCurrentTheme($theme); + } + + $files = Attach::selectToArray(['id', 'filename', 'filetype'], ['uid' => $this->session->getLocalUserId()]); + + + $fileArray = array_map([$this, 'map_files'], $files); + + $tpl = Renderer::getMarkupTemplate('profile/filebrowser.tpl'); + $output = Renderer::replaceMacros($tpl, [ + '$type' => 'attachment', + '$path' => ['' => $this->t('Files')], + '$folders' => false, + '$files' => $fileArray, + '$cancel' => $this->t('Cancel'), + '$nickname' => $this->app->getLoggedInUserNickname(), + '$upload' => $this->t('Upload'), + ]); + + if (empty($request['mode'])) { + System::httpExit($output); + } + + return $output; + } + + protected function map_files(array $record): array + { + [$m1, $m2] = explode('/', $record['filetype']); + $filetype = file_exists(sprintf('images/icons/%s.png', $m1) ? $m1 : 'zip'); + + return [ + sprintf('%s/attach/%s', $this->baseUrl, $record['id']), + $record['filename'], + sprintf('%s/images/icon/16/%s.png', $this->baseUrl, $filetype), + ]; + } +} diff --git a/src/Module/Profile/Photos/Browser.php b/src/Module/Profile/Photos/Browser.php new file mode 100644 index 000000000..1e8a28b12 --- /dev/null +++ b/src/Module/Profile/Photos/Browser.php @@ -0,0 +1,105 @@ +session = $session; + $this->app = $app; + } + + protected function content(array $request = []): string + { + if (!$this->session->getLocalUserId()) { + $this->baseUrl->redirect(); + } + + // Needed to match the correct template in a module that uses a different theme than the user/site/default + $theme = Strings::sanitizeFilePathItem($request['theme'] ?? ''); + if ($theme && is_file("view/theme/$theme/config.php")) { + $this->app->setCurrentTheme($theme); + } + + $album = $this->parameters['album'] ?? null; + + $photos = Photo::getBrowsablePhotosForUser($this->session->getLocalUserId(), $album); + $albums = $album ? false : Photo::getBrowsableAlbumsForUser($this->session->getLocalUserId()); + + $path = [ + '' => $this->t('Photos'), + ]; + if (!empty($album)) { + $path[$album] = $album; + } + + $photosArray = array_map([$this, 'map_files'], $photos); + + $tpl = Renderer::getMarkupTemplate('profile/filebrowser.tpl'); + $output = Renderer::replaceMacros($tpl, [ + '$type' => 'photos', + '$path' => $path, + '$folders' => $albums, + '$files' => $photosArray, + '$cancel' => $this->t('Cancel'), + '$nickname' => $this->app->getLoggedInUserNickname(), + '$upload' => $this->t('Upload'), + ]); + + if (empty($request['mode'])) { + System::httpExit($output); + } + + return $output; + } + + protected function map_files(array $record): array + { + $types = Images::supportedTypes(); + $ext = $types[$record['type']]; + $filename_e = $record['filename']; + + // Take the largest picture that is smaller or equal 640 pixels + $photo = Photo::selectFirst( + ['scale'], + [ + "`resource-id` = ? AND `height` <= ? AND `width` <= ?", + $record['resource-id'], + 640, + 640 + ], + ['order' => ['scale']]); + $scale = $photo['scale'] ?? $record['loq']; + + return [ + sprintf('%s/photos/%s/image/%s', $this->baseUrl, $this->app->getLoggedInUserNickname(), $record['resource-id']), + $filename_e, + sprintf('%s/photo/%s-%s.%s', $this->baseUrl, $record['resource-id'], $scale, $ext), + $record['desc'], + ]; + } +} diff --git a/static/routes.config.php b/static/routes.config.php index ae5755711..794f705e4 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -31,18 +31,20 @@ use Friendica\App\Router as R; use Friendica\Module; $profileRoutes = [ - '' => [Module\Profile\Index::class, [R::GET]], - '/attachment/upload' => [Module\Profile\Attachment\Upload::class, [ R::POST]], - '/contacts/common' => [Module\Profile\Common::class, [R::GET]], - '/contacts[/{type}]' => [Module\Profile\Contacts::class, [R::GET]], - '/media' => [Module\Profile\Media::class, [R::GET]], - '/photos' => [Module\Profile\Photos\Index::class, [R::GET ]], - '/photos/upload' => [Module\Profile\Photos\Upload::class, [ R::POST]], - '/profile' => [Module\Profile\Profile::class, [R::GET]], - '/remote_follow' => [Module\Profile\RemoteFollow::class, [R::GET, R::POST]], - '/schedule' => [Module\Profile\Schedule::class, [R::GET, R::POST]], - '/status[/{category}[/{date1}[/{date2}]]]' => [Module\Profile\Status::class, [R::GET]], - '/unkmail' => [Module\Profile\UnkMail::class, [R::GET, R::POST]], + '' => [Module\Profile\Index::class, [R::GET]], + '/attachment/upload' => [Module\Profile\Attachment\Upload::class, [ R::POST]], + '/attachment/browser' => [Module\Profile\Attachment\Browser::class, [R::GET]], + '/contacts/common' => [Module\Profile\Common::class, [R::GET]], + '/contacts[/{type}]' => [Module\Profile\Contacts::class, [R::GET]], + '/media' => [Module\Profile\Media::class, [R::GET]], + '/photos' => [Module\Profile\Photos\Index::class, [R::GET ]], + '/photos/browser[/{album}]' => [Module\Profile\Photos\Browser::class, [R::GET]], + '/photos/upload' => [Module\Profile\Photos\Upload::class, [ R::POST]], + '/profile' => [Module\Profile\Profile::class, [R::GET]], + '/remote_follow' => [Module\Profile\RemoteFollow::class, [R::GET, R::POST]], + '/schedule' => [Module\Profile\Schedule::class, [R::GET, R::POST]], + '/status[/{category}[/{date1}[/{date2}]]]' => [Module\Profile\Status::class, [R::GET]], + '/unkmail' => [Module\Profile\UnkMail::class, [R::GET, R::POST]], ]; $apiRoutes = [ diff --git a/view/global.css b/view/global.css index 800a3ea34..f09d95939 100644 --- a/view/global.css +++ b/view/global.css @@ -345,12 +345,12 @@ img.acpopup-img { .fbrowser .path a:before, .fbrowser .path .btn-link:before { content: "/"; padding-right: 5px;} .fbrowser .folders ul { list-style-type: none; padding-left: 10px;} .fbrowser .list { height: auto; overflow-y: hidden; margin: 10px 0px; } -.fbrowser.image .photo-album-image-wrapper { float: left; } -.fbrowser.image a img, .fbrowser.image .btn-link img { height: 48px; } -.fbrowser.image a p, .fbrowser.image .btn-link p { display: none;} -.fbrowser.file .photo-album-image-wrapper { float:none; white-space: nowrap; } -.fbrowser.file img { display: inline; } -.fbrowser.file p { display: inline; white-space: nowrap; } +.fbrowser.photos .photo-album-image-wrapper { float: left; } +.fbrowser.photos a img, .fbrowser.photos .btn-link img { height: 48px; } +.fbrowser.photos a p, .fbrowser.photos .btn-link p { display: none;} +.fbrowser.attachment .photo-album-image-wrapper { float:none; white-space: nowrap; } +.fbrowser.attachment img { display: inline; } +.fbrowser.attachment p { display: inline; white-space: nowrap; } .fbrowser .upload { clear: both; padding-top: 1em;} .fbrowser .error { background: #ffeeee; border: 1px solid #994444; color: #994444; padding: 0.5em;} .fbrowser .error .close { float: right; font-weight: bold; } diff --git a/view/js/filebrowser.js b/view/js/filebrowser.js index e5bd2730c..1b8a4d51f 100644 --- a/view/js/filebrowser.js +++ b/view/js/filebrowser.js @@ -33,7 +33,7 @@ * will be one of "image" or "file", and the event handler will * get the following params: * - * filemane: filename of item choosed by user + * filename: filename of item chosen by user * embed: bbcode to embed element into posts * id: id from caller code * @@ -47,52 +47,51 @@ * $("body").on("fbrowser.image.example", function(event, filename, bbcode, id) { * // close colorbox * $.colorbox.close(); - * // replace textxarea text with bbcode + * // replace textarea text with bbcode * $(id).value = bbcode; * }); **/ +const FileBrowser = { + nickname: '', + type: '', + event: '', + id: null, -var FileBrowser = { - nickname : "", - type : "", - event: "", - id : null, - - init: function(nickname, type) { + init: function (nickname, type) { FileBrowser.nickname = nickname; FileBrowser.type = type; - FileBrowser.event = "fbrowser."+type; - if (location['hash']!=="") { - var h = location['hash'].replace("#",""); + FileBrowser.event = "fbrowser." + type; + if (location['hash'] !== "") { + const h = location['hash'].replace('#', ''); FileBrowser.event = FileBrowser.event + "." + h.split("-")[0]; FileBrowser.id = h.split("-")[1]; } - console.log("FileBrowser:", nickname, type,FileBrowser.event, FileBrowser.id ); + console.log('FileBrowser:', nickname, type, FileBrowser.event, FileBrowser.id); - $(".error a.close").on("click", function(e) { + $('.error a.close').on('click', function (e) { e.preventDefault(); - $(".error").addClass("hidden"); + $('.error').addClass('hidden'); }); - $(".folders a, .path a").on("click", function(e){ + $('.folders a, .path a').on('click', function (e) { e.preventDefault(); - location.href = baseurl + "/fbrowser/" + FileBrowser.type + "/" + encodeURIComponent(this.dataset.folder) + "?mode=minimal" + location['hash']; + location.href = FileBrowser._getUrl("minimal", location['hash'], this.dataset.folder); + location.reload(); }); - $(".photo-album-photo-link").on('click', function(e){ + $(".photo-album-photo-link").on('click', function (e) { e.preventDefault(); - var embed = ""; - if (FileBrowser.type == "image") { - embed = "[url="+this.dataset.link+"][img="+this.dataset.img+"]"+this.dataset.alt+"[/img][/url]"; + let embed = ''; + if (FileBrowser.type === "photos") { + embed = '[url=' + this.dataset.link + '][img=' + this.dataset.img + ']' + this.dataset.alt + '[/img][/url]'; } - if (FileBrowser.type=="file") { - // attachment links are "baseurl/attach/id"; we need id - embed = "[attachment]"+this.dataset.link.split("/").pop()+"[/attachment]"; + if (FileBrowser.type === "attachment") { + embed = '[attachment]' + this.dataset.link + '[/attachment]'; } console.log(FileBrowser.event, this.dataset.filename, embed, FileBrowser.id); - parent.$("body").trigger(FileBrowser.event, [ + parent.$('body').trigger(FileBrowser.event, [ this.dataset.filename, embed, FileBrowser.id @@ -100,45 +99,63 @@ var FileBrowser = { }); - if ($("#upload-image").length) - var image_uploader = new window.AjaxUpload( - 'upload-image', - { action: 'profile/' + FileBrowser.nickname + '/photos/upload?response=json', + if ($('#upload-photos').length) + { + new window.AjaxUpload( + 'upload-photos', + { + action: 'profile/' + FileBrowser.nickname + '/photos/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) { - $(".error span").html(response['error']); - $(".error").removeClass('hidden'); + onSubmit: function (file, ext) { + $('#profile-rotator').show(); + $('.error').addClass('hidden'); + }, + onComplete: function (file, response) { + if (response['error'] !== undefined) { + $('.error span').html(response['error']); + $('.error').removeClass('hidden'); $('#profile-rotator').hide(); return; } - location = baseurl + "/fbrowser/image/?mode=minimal"+location['hash']; - location.reload(true); + location.href = FileBrowser._getUrl("minimal", location['hash']); + location.reload(); } } ); + } - if ($("#upload-file").length) - var file_uploader = new window.AjaxUpload( - 'upload-file', - { action: 'profile/' + FileBrowser.nickname + '/attachment/upload?response=json', + if ($('#upload-attachment').length) + { + new window.AjaxUpload( + 'upload-attachment', + { + 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) { - $(".error span").html(response['error']); - $(".error").removeClass('hidden'); + onSubmit: function (file, ext) { + $('#profile-rotator').show(); + $('.error').addClass('hidden'); + }, + onComplete: function (file, response) { + if (response['error'] !== undefined) { + $('.error span').html(response['error']); + $('.error').removeClass('hidden'); $('#profile-rotator').hide(); return; } - location = baseurl + "/fbrowser/file/?mode=minimal"+location['hash']; - location.reload(true); + location.href = FileBrowser._getUrl("minimal", location['hash']); + location.reload(); } } - ); + ); + } + }, + + _getUrl: function (mode, hash, folder) { + let folderValue = folder !== undefined ? folder : FileBrowser.folder; + let folderUrl = folderValue !== undefined ? '/' + encodeURIComponent(folderValue) : ''; + return 'profile/' + FileBrowser.nickname + '/' + FileBrowser.type + '/browser' + folderUrl + '?mode=' + mode + hash; } }; // @license-end diff --git a/view/js/main.js b/view/js/main.js index 93340dc37..3ba8240fe 100644 --- a/view/js/main.js +++ b/view/js/main.js @@ -166,7 +166,7 @@ $(function() { /* event from comment textarea button popups */ /* insert returned bbcode at cursor position or replace selected text */ - $("body").on("fbrowser.image.comment", function(e, filename, bbcode, id) { + $("body").on("fbrowser.photos.comment", function(e, filename, bbcode, id) { $.colorbox.close(); var textarea = document.getElementById("comment-edit-text-" +id); var start = textarea.selectionStart; @@ -1069,7 +1069,7 @@ var Dialog = { * to the event handler */ doImageBrowser : function (name, id) { - var url = Dialog._get_url("image",name,id); + var url = Dialog._get_url("photos",name,id); return Dialog.show(url); }, @@ -1086,7 +1086,7 @@ var Dialog = { * to the event handler */ doFileBrowser : function (name, id) { - var url = Dialog._get_url("file",name,id); + var url = Dialog._get_url("attachment",name,id); return Dialog.show(url); }, @@ -1095,7 +1095,7 @@ var Dialog = { if (id !== undefined) { hash = hash + "-" + id; } - return baseurl + "/fbrowser/"+type+"/?mode=minimal#"+hash; + return '/profile/' + localNickname + '/' + type + '/browser?mode=minimal#' + hash; }, _get_size: function() { diff --git a/view/templates/jot-header.tpl b/view/templates/jot-header.tpl index a67f9c3f0..2a81db6dd 100644 --- a/view/templates/jot-header.tpl +++ b/view/templates/jot-header.tpl @@ -61,11 +61,11 @@ function enableOnUser(){ **/ /* callback */ - $('body').on('fbrowser.image.main', function(e, filename, embedcode, id) { + $('body').on('fbrowser.photos.main', function(e, filename, embedcode, id) { $.colorbox.close(); addeditortext(embedcode); }); - $('body').on('fbrowser.file.main', function(e, filename, embedcode, id) { + $('body').on('fbrowser.photos.main', function(e, filename, embedcode, id) { $.colorbox.close(); addeditortext(embedcode); }); diff --git a/view/templates/filebrowser.tpl b/view/templates/profile/filebrowser.tpl similarity index 80% rename from view/templates/filebrowser.tpl rename to view/templates/profile/filebrowser.tpl index 09bf563ee..ff25741a9 100644 --- a/view/templates/filebrowser.tpl +++ b/view/templates/profile/filebrowser.tpl @@ -1,8 +1,8 @@ - - + + - + - +
diff --git a/view/theme/frio/js/modal.js b/view/theme/frio/js/modal.js index ed256450c..c3c4009b0 100644 --- a/view/theme/frio/js/modal.js +++ b/view/theme/frio/js/modal.js @@ -166,8 +166,8 @@ Dialog._load = function (url) { // Initialize the filebrowser. loadScript("view/js/ajaxupload.js"); - loadScript("view/theme/frio/js/module/media/filebrowser.js", function () { - FileBrowser.init(filebrowser.dataset.nickname, filebrowser.dataset.type, match[1]); + loadScript("view/theme/frio/js/module/media/browser.js", function () { + Browser.init(filebrowser.dataset.nickname, filebrowser.dataset.type, match[1]); }); }; diff --git a/view/theme/frio/js/module/media/filebrowser.js b/view/theme/frio/js/module/media/browser.js similarity index 78% rename from view/theme/frio/js/module/media/filebrowser.js rename to view/theme/frio/js/module/media/browser.js index 802c44098..c89f426eb 100644 --- a/view/theme/frio/js/module/media/filebrowser.js +++ b/view/theme/frio/js/module/media/browser.js @@ -59,11 +59,11 @@ * the frio theme.and bootstrap modals * * The original file is under: - * js/filebrowser.js + * js/module/media/browser.js * */ -var FileBrowser = { +var Browser = { nickname: '', type: '', event: '', @@ -71,24 +71,24 @@ var FileBrowser = { id: null, init: function (nickname, type, hash) { - FileBrowser.nickname = nickname; - FileBrowser.type = type; - FileBrowser.event = 'fbrowser.' + type; + Browser.nickname = nickname; + Browser.type = type; + Browser.event = 'fbrowser.' + type; if (hash !== '') { const h = hash.replace('#', ''); const destination = h.split('-')[0]; - FileBrowser.id = h.split('-')[1]; - FileBrowser.event = FileBrowser.event + '.' + destination; + Browser.id = h.split('-')[1]; + Browser.event = Browser.event + '.' + destination; if (destination === 'comment') { // Get the comment textinput field - var commentElm = document.getElementById('comment-edit-text-' + FileBrowser.id); + var commentElm = document.getElementById('comment-edit-text-' + Browser.id); } } - console.log('FileBrowser: ' + nickname, type, FileBrowser.event, FileBrowser.id); + console.log('FileBrowser: ' + nickname, type, Browser.event, Browser.id); - FileBrowser.postLoad(); + Browser.postLoad(); $('.error .close').on('click', function (e) { e.preventDefault(); @@ -98,10 +98,10 @@ var FileBrowser = { // Click on album link $('.fbrowser').on('click', '.folders button, .path button', function (e) { e.preventDefault(); - let url = FileBrowser._getUrl("none", this.dataset.folder); - FileBrowser.folder = this.dataset.folder; + let url = Browser._getUrl("none", this.dataset.folder); + Browser.folder = this.dataset.folder; - FileBrowser.loadContent(url); + Browser.loadContent(url); }); //Embed on click @@ -109,10 +109,10 @@ var FileBrowser = { e.preventDefault(); let embed = ''; - if (FileBrowser.type === 'photo') { + if (Browser.type === 'photo') { embed = '[url=' + this.dataset.link + '][img=' + this.dataset.img + ']' + this.dataset.alt + '[/img][/url]'; } - if (FileBrowser.type === 'attachment') { + if (Browser.type === 'attachment') { embed = '[attachment]' + this.dataset.link + '[/attachment]'; } @@ -122,18 +122,18 @@ var FileBrowser = { // As for now we insert pieces of this function here if (commentElm !== null && typeof commentElm !== 'undefined') { if (commentElm.value === '') { - $('#comment-edit-text-' + FileBrowser.id) + $('#comment-edit-text-' + Browser.id) .addClass('comment-edit-text-full') .removeClass('comment-edit-text-empty'); - $('#comment-edit-submit-wrapper-' + FileBrowser.id).show(); - $('#comment-edit-text-' + FileBrowser.id).attr('tabindex', '9'); - $('#comment-edit-submit-' + FileBrowser.id).attr('tabindex', '10'); + $('#comment-edit-submit-wrapper-' + Browser.id).show(); + $('#comment-edit-text-' + Browser.id).attr('tabindex', '9'); + $('#comment-edit-submit-' + Browser.id).attr('tabindex', '10'); } } - console.log(FileBrowser.event, this.dataset.filename, embed, FileBrowser.id); + console.log(Browser.event, this.dataset.filename, embed, Browser.id); - $('body').trigger(FileBrowser.event, [this.dataset.filename, embed, FileBrowser.id, this.dataset.img]); + $('body').trigger(Browser.event, [this.dataset.filename, embed, Browser.id, this.dataset.img]); // Close model $('#modal').modal('hide'); @@ -144,12 +144,12 @@ var FileBrowser = { // EventListener for switching between photo and file mode $('.fbrowser').on('click', '.fbswitcher .btn', function (e) { e.preventDefault(); - FileBrowser.type = this.getAttribute('data-mode'); + Browser.type = this.getAttribute('data-mode'); $('.fbrowser') .removeClass() - .addClass('fbrowser ' + FileBrowser.type); + .addClass('fbrowser ' + Browser.type); - FileBrowser.loadContent(FileBrowser._getUrl("none")); + Browser.loadContent(Browser._getUrl("none")); }); }, @@ -160,7 +160,7 @@ var FileBrowser = { new window.AjaxUpload( 'upload-photo', { - action: 'media/photo/upload?response=json&album=' + encodeURIComponent(FileBrowser.folder), + action: 'media/photo/upload?response=json&album=' + encodeURIComponent(Browser.folder), name: 'userfile', responseType: 'json', onSubmit: function (file, ext) { @@ -177,7 +177,7 @@ var FileBrowser = { return; } // load new content to fbrowser window - FileBrowser.loadContent(FileBrowser._getUrl("none")); + Browser.loadContent(Browser._getUrl("none")); }, }); } @@ -204,7 +204,7 @@ var FileBrowser = { return; } // Load new content to fbrowser window - FileBrowser.loadContent(FileBrowser._getUrl("none")); + Browser.loadContent(Browser._getUrl("none")); }, }); } @@ -212,11 +212,11 @@ var FileBrowser = { // Stuff which should be executed if no content was loaded postLoad: function () { - FileBrowser.initGallery(); + Browser.initGallery(); $('.fbrowser .fbswitcher .btn').removeClass('active'); - $('.fbrowser .fbswitcher [data-mode=' + FileBrowser.type + ']').addClass('active'); + $('.fbrowser .fbswitcher [data-mode=' + Browser.type + ']').addClass('active'); // We need to add the AjaxUpload to the button - FileBrowser.uploadButtons(); + Browser.uploadButtons(); }, // Load new content (e.g. change photo album) @@ -229,7 +229,7 @@ var FileBrowser = { $('.profile-rotator-wrapper').hide(); if (textStatus === 'success') { $(".fbrowser_content").show(); - FileBrowser.postLoad(); + Browser.postLoad(); } }); }, @@ -244,9 +244,9 @@ var FileBrowser = { }, _getUrl: function (mode, folder) { - let folderValue = folder !== undefined ? folder : FileBrowser.folder; + let folderValue = folder !== undefined ? folder : Browser.folder; let folderUrl = folderValue !== undefined ? '/' + encodeURIComponent(folderValue) : ''; - return 'media/' + FileBrowser.type + '/browser' + folderUrl + '?mode=' + mode + "&theme=frio"; + return 'media/' + Browser.type + '/browser' + folderUrl + '?mode=' + mode + "&theme=frio"; } }; // @license-end diff --git a/view/theme/frio/templates/media/filebrowser.tpl b/view/theme/frio/templates/media/browser.tpl similarity index 100% rename from view/theme/frio/templates/media/filebrowser.tpl rename to view/theme/frio/templates/media/browser.tpl From 29190fae73e6b9cf7a4dbca47263bcc1da431628 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 27 Nov 2022 01:36:31 +0100 Subject: [PATCH 17/23] Update messages.po --- src/Module/Media/Attachment/Browser.php | 2 +- view/lang/C/messages.po | 155 ++++++++++++------------ 2 files changed, 80 insertions(+), 77 deletions(-) diff --git a/src/Module/Media/Attachment/Browser.php b/src/Module/Media/Attachment/Browser.php index de9f7f4fc..ed9ae74a3 100644 --- a/src/Module/Media/Attachment/Browser.php +++ b/src/Module/Media/Attachment/Browser.php @@ -89,7 +89,7 @@ class Browser extends BaseModule protected function map_files(array $record): array { - [$m1, $m2] = explode('/', $record['filetype']); + list($m1, $m2) = explode('/', $record['filetype']); $filetype = file_exists(sprintf('images/icons/%s.png', $m1) ? $m1 : 'text'); return [ diff --git a/view/lang/C/messages.po b/view/lang/C/messages.po index 14ac264f1..b9115ad4f 100644 --- a/view/lang/C/messages.po +++ b/view/lang/C/messages.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: 2022.12-dev\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-11-23 18:16+0100\n" +"POT-Creation-Date: 2022-11-27 00:36+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -18,36 +18,13 @@ msgstr "" "Content-Transfer-Encoding: 8bit\n" -#: mod/fbrowser.php:61 src/Content/Nav.php:195 src/Module/BaseProfile.php:64 -#: view/theme/frio/theme.php:242 -msgid "Photos" -msgstr "" - -#: mod/fbrowser.php:119 mod/fbrowser.php:146 mod/photos.php:997 -#: mod/photos.php:1098 src/Content/Conversation.php:389 -#: src/Module/Contact/Follow.php:173 src/Module/Contact/Revoke.php:109 -#: src/Module/Contact/Unfollow.php:126 src/Module/Post/Edit.php:164 -#: src/Module/Post/Tag/Remove.php:109 src/Module/Profile/RemoteFollow.php:134 -#: src/Module/Security/TwoFactor/SignOut.php:125 -msgid "Cancel" -msgstr "" - -#: mod/fbrowser.php:121 mod/fbrowser.php:148 -#: src/Module/Settings/Profile/Photo/Index.php:128 -msgid "Upload" -msgstr "" - -#: mod/fbrowser.php:143 -msgid "Files" -msgstr "" - #: mod/item.php:129 mod/item.php:133 msgid "Unable to locate original post." msgstr "" -#: mod/item.php:179 mod/item.php:184 mod/item.php:853 mod/message.php:69 +#: mod/item.php:179 mod/item.php:184 mod/item.php:855 mod/message.php:69 #: mod/message.php:114 mod/notes.php:44 mod/photos.php:159 mod/photos.php:884 -#: src/Module/Attach.php:56 src/Module/BaseApi.php:94 +#: src/Module/Attach.php:55 src/Module/BaseApi.php:94 #: src/Module/BaseNotifications.php:98 src/Module/BaseSettings.php:52 #: src/Module/Calendar/Event/API.php:88 src/Module/Calendar/Event/Form.php:84 #: src/Module/Calendar/Event/Show.php:54 src/Module/Calendar/Export.php:62 @@ -62,9 +39,8 @@ msgstr "" #: src/Module/Notifications/Notification.php:76 #: src/Module/Notifications/Notification.php:107 #: src/Module/OStatus/Repair.php:60 src/Module/OStatus/Subscribe.php:66 -#: src/Module/Post/Edit.php:76 src/Module/Profile/Attachment/Upload.php:97 -#: src/Module/Profile/Common.php:55 src/Module/Profile/Contacts.php:55 -#: src/Module/Profile/Photos/Upload.php:108 src/Module/Profile/Schedule.php:39 +#: src/Module/Post/Edit.php:76 src/Module/Profile/Common.php:55 +#: src/Module/Profile/Contacts.php:55 src/Module/Profile/Schedule.php:39 #: src/Module/Profile/Schedule.php:56 src/Module/Profile/UnkMail.php:69 #: src/Module/Profile/UnkMail.php:121 src/Module/Profile/UnkMail.php:132 #: src/Module/Register.php:77 src/Module/Register.php:90 @@ -84,23 +60,23 @@ msgstr "" msgid "Permission denied." msgstr "" -#: mod/item.php:328 mod/item.php:333 +#: mod/item.php:330 mod/item.php:335 msgid "Empty post discarded." msgstr "" -#: mod/item.php:671 +#: mod/item.php:673 msgid "Post updated." msgstr "" -#: mod/item.php:681 mod/item.php:686 +#: mod/item.php:683 mod/item.php:688 msgid "Item wasn't stored." msgstr "" -#: mod/item.php:697 +#: mod/item.php:699 msgid "Item couldn't be fetched." msgstr "" -#: mod/item.php:829 src/Module/Admin/Themes/Details.php:39 +#: mod/item.php:831 src/Module/Admin/Themes/Details.php:39 #: src/Module/Admin/Themes/Index.php:59 src/Module/Debug/ItemBody.php:42 #: src/Module/Debug/ItemBody.php:57 src/Module/Item/Feed.php:80 msgid "Item not found." @@ -414,31 +390,28 @@ msgstr "" #: src/Module/HCard.php:51 src/Module/Profile/Common.php:40 #: src/Module/Profile/Common.php:51 src/Module/Profile/Contacts.php:39 #: src/Module/Profile/Contacts.php:49 src/Module/Profile/Media.php:38 -#: src/Module/Profile/Photos/Index.php:77 -#: src/Module/Profile/Photos/Upload.php:87 -#: src/Module/Profile/RemoteFollow.php:71 src/Module/Profile/Status.php:58 -#: src/Module/Register.php:267 +#: src/Module/Profile/Photos.php:77 src/Module/Profile/RemoteFollow.php:71 +#: src/Module/Profile/Status.php:58 src/Module/Register.php:267 msgid "User not found." msgstr "" #: mod/photos.php:107 src/Module/BaseProfile.php:67 -#: src/Module/Profile/Photos/Index.php:169 +#: src/Module/Profile/Photos.php:169 msgid "Photo Albums" msgstr "" -#: mod/photos.php:108 src/Module/Profile/Photos/Index.php:170 -#: src/Module/Profile/Photos/Index.php:187 +#: mod/photos.php:108 src/Module/Profile/Photos.php:170 +#: src/Module/Profile/Photos.php:187 msgid "Recent Photos" msgstr "" -#: mod/photos.php:110 mod/photos.php:1066 -#: src/Module/Profile/Photos/Index.php:172 -#: src/Module/Profile/Photos/Index.php:189 +#: mod/photos.php:110 mod/photos.php:1066 src/Module/Profile/Photos.php:172 +#: src/Module/Profile/Photos.php:189 msgid "Upload New Photos" msgstr "" #: mod/photos.php:128 src/Module/BaseSettings.php:74 -#: src/Module/Profile/Photos/Index.php:153 +#: src/Module/Profile/Photos.php:153 msgid "everybody" msgstr "" @@ -472,7 +445,7 @@ msgid "%1$s was tagged in %2$s by %3$s" msgstr "" #: mod/photos.php:630 mod/photos.php:633 mod/photos.php:660 -#: src/Module/Profile/Photos/Upload.php:213 +#: src/Module/Media/Photo/Upload.php:188 #: src/Module/Settings/Profile/Photo/Index.php:59 #, php-format msgid "Image exceeds size limit of %s" @@ -496,19 +469,19 @@ msgstr "" msgid "Image file is empty." msgstr "" -#: mod/photos.php:683 src/Module/Profile/Photos/Upload.php:179 -#: src/Module/Profile/Photos/Upload.php:180 +#: mod/photos.php:683 src/Module/Media/Photo/Upload.php:154 +#: src/Module/Media/Photo/Upload.php:155 #: src/Module/Settings/Profile/Photo/Index.php:68 msgid "Unable to process image." msgstr "" -#: mod/photos.php:709 src/Module/Profile/Photos/Upload.php:231 +#: mod/photos.php:709 src/Module/Media/Photo/Upload.php:206 #: src/Module/Settings/Profile/Photo/Index.php:95 msgid "Image upload failed." msgstr "" #: mod/photos.php:795 src/Module/Conversation/Community.php:187 -#: src/Module/Directory.php:48 src/Module/Profile/Photos/Index.php:72 +#: src/Module/Directory.php:48 src/Module/Profile/Photos.php:72 #: src/Module/Search/Index.php:64 msgid "Public access denied." msgstr "" @@ -517,7 +490,7 @@ msgstr "" msgid "No photos selected" msgstr "" -#: mod/photos.php:869 src/Module/Profile/Photos/Index.php:92 +#: mod/photos.php:869 src/Module/Profile/Photos.php:92 msgid "Access to this item is restricted." msgstr "" @@ -550,6 +523,16 @@ msgstr "" msgid "Delete Album" msgstr "" +#: mod/photos.php:997 mod/photos.php:1098 src/Content/Conversation.php:389 +#: src/Module/Contact/Follow.php:173 src/Module/Contact/Revoke.php:109 +#: src/Module/Contact/Unfollow.php:126 +#: src/Module/Media/Attachment/Browser.php:78 +#: src/Module/Media/Photo/Browser.php:88 src/Module/Post/Edit.php:164 +#: src/Module/Post/Tag/Remove.php:109 src/Module/Profile/RemoteFollow.php:134 +#: src/Module/Security/TwoFactor/SignOut.php:125 +msgid "Cancel" +msgstr "" + #: mod/photos.php:1023 msgid "Edit Album" msgstr "" @@ -566,7 +549,7 @@ msgstr "" msgid "Show Oldest First" msgstr "" -#: mod/photos.php:1051 src/Module/Profile/Photos/Index.php:140 +#: mod/photos.php:1051 src/Module/Profile/Photos.php:140 msgid "View Photo" msgstr "" @@ -1686,6 +1669,11 @@ msgstr "" msgid "Your profile page" msgstr "" +#: src/Content/Nav.php:195 src/Module/BaseProfile.php:64 +#: src/Module/Media/Photo/Browser.php:74 view/theme/frio/theme.php:242 +msgid "Photos" +msgstr "" + #: src/Content/Nav.php:195 view/theme/frio/theme.php:242 msgid "Your photos" msgstr "" @@ -3180,7 +3168,7 @@ msgstr "" msgid "[no subject]" msgstr "" -#: src/Model/Photo.php:1086 src/Module/Profile/Photos/Upload.php:223 +#: src/Model/Photo.php:1139 src/Module/Media/Photo/Upload.php:198 msgid "Wall Photos" msgstr "" @@ -5265,7 +5253,7 @@ msgstr "" msgid "Applications" msgstr "" -#: src/Module/Attach.php:50 src/Module/Attach.php:62 +#: src/Module/Attach.php:49 src/Module/Attach.php:61 msgid "Item was not found." msgstr "" @@ -5894,10 +5882,10 @@ msgid "The contact could not be added." msgstr "" #: src/Module/Contact/MatchInterests.php:94 -#: src/Module/Profile/Attachment/Upload.php:80 -#: src/Module/Profile/Attachment/Upload.php:102 -#: src/Module/Profile/Photos/Upload.php:113 -#: src/Module/Profile/Photos/Upload.php:162 +#: src/Module/Media/Attachment/Upload.php:80 +#: src/Module/Media/Attachment/Upload.php:85 +#: src/Module/Media/Photo/Upload.php:83 src/Module/Media/Photo/Upload.php:88 +#: src/Module/Media/Photo/Upload.php:137 msgid "Invalid request." msgstr "" @@ -7129,6 +7117,38 @@ msgstr "" msgid "A Decentralized Social Network" msgstr "" +#: src/Module/Media/Attachment/Browser.php:58 +#: src/Module/Media/Photo/Browser.php:59 +msgid "You need to be logged in to access this page." +msgstr "" + +#: src/Module/Media/Attachment/Browser.php:75 +msgid "Files" +msgstr "" + +#: src/Module/Media/Attachment/Browser.php:80 +#: src/Module/Media/Photo/Browser.php:90 +#: src/Module/Settings/Profile/Photo/Index.php:128 +msgid "Upload" +msgstr "" + +#: src/Module/Media/Attachment/Upload.php:100 +msgid "Sorry, maybe your upload is bigger than the PHP configuration allows" +msgstr "" + +#: src/Module/Media/Attachment/Upload.php:100 +msgid "Or - did you try to upload an empty file?" +msgstr "" + +#: src/Module/Media/Attachment/Upload.php:107 +#, php-format +msgid "File exceeds size limit of %s" +msgstr "" + +#: src/Module/Media/Attachment/Upload.php:117 +msgid "File upload failed." +msgstr "" + #: src/Module/Moderation/BaseUsers.php:72 msgid "List of all users" msgstr "" @@ -8134,28 +8154,11 @@ msgstr "" msgid "Remove" msgstr "" -#: src/Module/Profile/Attachment/Upload.php:117 -msgid "Sorry, maybe your upload is bigger than the PHP configuration allows" -msgstr "" - -#: src/Module/Profile/Attachment/Upload.php:117 -msgid "Or - did you try to upload an empty file?" -msgstr "" - -#: src/Module/Profile/Attachment/Upload.php:124 -#, php-format -msgid "File exceeds size limit of %s" -msgstr "" - -#: src/Module/Profile/Attachment/Upload.php:134 -msgid "File upload failed." -msgstr "" - #: src/Module/Profile/Contacts.php:119 msgid "No contacts." msgstr "" -#: src/Module/Profile/Photos/Index.php:146 +#: src/Module/Profile/Photos.php:146 msgid "View Album" msgstr "" From 4e53ba0c20aef86d7a5ef202faed498b4181bb76 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 27 Nov 2022 01:44:12 +0100 Subject: [PATCH 18/23] Cache the Module class --- src/App/Router.php | 40 ++++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/App/Router.php b/src/App/Router.php index b6fd1098f..35ea9ada8 100644 --- a/src/App/Router.php +++ b/src/App/Router.php @@ -40,6 +40,7 @@ use Friendica\Module\HTTPException\MethodNotAllowed; use Friendica\Module\HTTPException\PageNotFound; use Friendica\Module\Special\Options; use Friendica\Network\HTTPException; +use Friendica\Network\HTTPException\InternalServerErrorException; use Friendica\Network\HTTPException\MethodNotAllowedException; use Friendica\Network\HTTPException\NotFoundException; use Friendica\Util\Router\FriendicaGroupCountBased; @@ -114,6 +115,9 @@ class Router /** @var array */ private $server; + /** @var string|null */ + protected $moduleClass = null; + /** * @param array $server The $_SERVER variable * @param string $baseRoutesFilepath The path to a base routes file to leverage cache, can be empty @@ -216,7 +220,7 @@ class Router * * @return bool */ - private function isGroup(array $config) + private function isGroup(array $config): bool { return is_array($config) && @@ -252,21 +256,39 @@ class Router * * @return RouteCollector|null */ - public function getRouteCollector() + public function getRouteCollector(): ?RouteCollector { return $this->routeCollector; } + /** + * Returns the Friendica\BaseModule-extending class name if a route rule matched + * + * @return string + * + * @throws InternalServerErrorException + * @throws MethodNotAllowedException + * @throws NotFoundException + */ + public function getModuleClass(): string + { + if (empty($this->moduleClass)) { + $this->determineModuleClass(); + } + + return $this->moduleClass; + } + /** * Returns the relevant module class name for the given page URI or NULL if no route rule matched. * - * @return string A Friendica\BaseModule-extending class name if a route rule matched + * @return void * * @throws HTTPException\InternalServerErrorException * @throws HTTPException\MethodNotAllowedException If a rule matched but the method didn't * @throws HTTPException\NotFoundException If no rule matched */ - public function getModuleClass(): string + private function determineModuleClass(): void { $cmd = $this->args->getCommand(); $cmd = '/' . ltrim($cmd, '/'); @@ -277,21 +299,19 @@ class Router // Check if the HTTP method is OPTIONS and return the special Options Module with the possible HTTP methods if ($this->args->getMethod() === static::OPTIONS) { - $moduleClass = Options::class; - $this->parameters = ['allowedMethods' => $dispatcher->getOptions($cmd)]; + $this->moduleClass = Options::class; + $this->parameters = ['allowedMethods' => $dispatcher->getOptions($cmd)]; } else { $routeInfo = $dispatcher->dispatch($this->args->getMethod(), $cmd); if ($routeInfo[0] === Dispatcher::FOUND) { - $moduleClass = $routeInfo[1]; - $this->parameters = $routeInfo[2]; + $this->moduleClass = $routeInfo[1]; + $this->parameters = $routeInfo[2]; } elseif ($routeInfo[0] === Dispatcher::METHOD_NOT_ALLOWED) { throw new HTTPException\MethodNotAllowedException($this->l10n->t('Method not allowed for this module. Allowed method(s): %s', implode(', ', $routeInfo[1]))); } else { throw new HTTPException\NotFoundException($this->l10n->t('Page not found.')); } } - - return $moduleClass; } public function getModule(?string $module_class = null): ICanHandleRequests From 272911527cc7dd5a32fdf1a24eca641ed31be402 Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 27 Nov 2022 01:52:14 +0100 Subject: [PATCH 19/23] Apply suggestions from code review Co-authored-by: Hypolite Petovan --- src/Module/Media/Attachment/Browser.php | 1 - src/Module/Media/Attachment/Upload.php | 1 - view/js/module/media/browser.js | 3 --- view/theme/frio/js/module/media/browser.js | 4 +--- 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/Module/Media/Attachment/Browser.php b/src/Module/Media/Attachment/Browser.php index ed9ae74a3..86ea00faf 100644 --- a/src/Module/Media/Attachment/Browser.php +++ b/src/Module/Media/Attachment/Browser.php @@ -66,7 +66,6 @@ class Browser extends BaseModule $files = Attach::selectToArray(['id', 'filename', 'filetype'], ['uid' => $this->session->getLocalUserId()]); - $fileArray = array_map([$this, 'map_files'], $files); $tpl = Renderer::getMarkupTemplate('media/browser.tpl'); diff --git a/src/Module/Media/Attachment/Upload.php b/src/Module/Media/Attachment/Upload.php index b11469f5a..0bc95fe79 100644 --- a/src/Module/Media/Attachment/Upload.php +++ b/src/Module/Media/Attachment/Upload.php @@ -74,7 +74,6 @@ class Upload extends \Friendica\BaseModule } $owner = User::getOwnerDataById($this->userSession->getLocalUserId()); - if (!$owner) { $this->logger->warning('Owner not found.', ['uid' => $this->userSession->getLocalUserId()]); return $this->return(401, $this->t('Invalid request.')); diff --git a/view/js/module/media/browser.js b/view/js/module/media/browser.js index 7790e2588..c78a7f13b 100644 --- a/view/js/module/media/browser.js +++ b/view/js/module/media/browser.js @@ -67,8 +67,6 @@ const Browser = { Browser.id = h.split('-')[1]; } - console.log('FileBrowser:', nickname, type, Browser.event, Browser.id); - $('.error a.close').on('click', function (e) { e.preventDefault(); $('.error').addClass('hidden'); @@ -90,7 +88,6 @@ const Browser = { if (Browser.type === "attachment") { embed = '[attachment]' + this.dataset.link + '[/attachment]'; } - console.log(Browser.event, this.dataset.filename, embed, Browser.id); parent.$('body').trigger(Browser.event, [ this.dataset.filename, embed, diff --git a/view/theme/frio/js/module/media/browser.js b/view/theme/frio/js/module/media/browser.js index c89f426eb..ccd2e1b5b 100644 --- a/view/theme/frio/js/module/media/browser.js +++ b/view/theme/frio/js/module/media/browser.js @@ -56,7 +56,7 @@ * IMPORTANT * * This is a modified version to work with - * the frio theme.and bootstrap modals + * the frio theme and Bootstrap modals * * The original file is under: * js/module/media/browser.js @@ -86,8 +86,6 @@ var Browser = { } } - console.log('FileBrowser: ' + nickname, type, Browser.event, Browser.id); - Browser.postLoad(); $('.error .close').on('click', function (e) { From 3ac3b3d2af2b8613978fce9f285a5afcc9e1352e Mon Sep 17 00:00:00 2001 From: Philipp Date: Sun, 27 Nov 2022 01:52:49 +0100 Subject: [PATCH 20/23] Perfection! --- src/Model/Photo.php | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/Model/Photo.php b/src/Model/Photo.php index 87a81069f..990fb7e61 100644 --- a/src/Model/Photo.php +++ b/src/Model/Photo.php @@ -206,8 +206,15 @@ class Photo */ public static function getBrowsablePhotosForUser(int $uid, string $album = null): array { + $values = [ + $uid, + Photo::CONTACT_AVATAR, + Photo::CONTACT_BANNER + ]; + if (!empty($album)) { - $sqlExtra = sprintf("AND `album` = '%s' ", DBA::escape($album)); + $sqlExtra = "AND `album` = ? "; + $values[] = $album; $sqlExtra2 = ""; } else { $sqlExtra = ''; @@ -218,11 +225,9 @@ class Photo DBA::p( "SELECT `resource-id`, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`filename`) AS `filename`, ANY_VALUE(`type`) AS `type`, min(`scale`) AS `hiq`, max(`scale`) AS `loq`, ANY_VALUE(`desc`) AS `desc`, ANY_VALUE(`created`) AS `created` - FROM `photo` WHERE `uid` = ? $sqlExtra AND NOT `photo-type` IN (?, ?) + FROM `photo` WHERE `uid` = ? AND NOT `photo-type` IN (?, ?) $sqlExtra GROUP BY `resource-id` $sqlExtra2", - $uid, - Photo::CONTACT_AVATAR, - Photo::CONTACT_BANNER + $values )); } From 25e14121747dc761f87c7f5218a359d9c82b8113 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 26 Nov 2022 21:48:26 -0500 Subject: [PATCH 21/23] Remove related notifications when marking an item for deletion --- src/Model/Item.php | 3 +++ .../Notifications/Repository/Notification.php | 20 +++++++++++++++++++ .../Notifications/Repository/Notify.php | 6 ++++++ 3 files changed, 29 insertions(+) diff --git a/src/Model/Item.php b/src/Model/Item.php index 0390730fd..cb998c0fd 100644 --- a/src/Model/Item.php +++ b/src/Model/Item.php @@ -385,6 +385,9 @@ class Item Post\ThreadUser::update($item['uri-id'], $item['uid'], ['hidden' => true]); } + DI::notify()->deleteForItem($item['uri-id']); + DI::notification()->deleteForItem($item['uri-id']); + Logger::info('Item has been marked for deletion.', ['id' => $item_id]); return true; diff --git a/src/Navigation/Notifications/Repository/Notification.php b/src/Navigation/Notifications/Repository/Notification.php index a9630464a..6a513c5dc 100644 --- a/src/Navigation/Notifications/Repository/Notification.php +++ b/src/Navigation/Notifications/Repository/Notification.php @@ -33,6 +33,7 @@ use Friendica\Navigation\Notifications\Collection; use Friendica\Navigation\Notifications\Entity; use Friendica\Navigation\Notifications\Factory; use Friendica\Network\HTTPException\NotFoundException; +use Friendica\Protocol\Activity; use Friendica\Util\DateTimeFormat; use Psr\Log\LoggerInterface; @@ -268,4 +269,23 @@ class Notification extends BaseRepository return $this->db->delete(self::$table_name, $condition); } + + public function deleteForItem(int $itemUriId): bool + { + $conditionTarget = [ + 'vid' => Verb::getID(Activity::POST), + 'target-uri-id' => $itemUriId, + ]; + + $conditionParent = [ + 'vid' => Verb::getID(Activity::POST), + 'parent-uri-id' => $itemUriId, + ]; + + $this->logger->notice('deleteForItem', ['conditionTarget' => $conditionTarget, 'conditionParent' => $conditionParent]); + + return + $this->db->delete(self::$table_name, $conditionTarget) + && $this->db->delete(self::$table_name, $conditionParent); + } } diff --git a/src/Navigation/Notifications/Repository/Notify.php b/src/Navigation/Notifications/Repository/Notify.php index 3891a6e25..75aff6b87 100644 --- a/src/Navigation/Notifications/Repository/Notify.php +++ b/src/Navigation/Notifications/Repository/Notify.php @@ -807,4 +807,10 @@ class Notify extends BaseRepository return $this->storeAndSend($params, $sitelink, $tsitelink, $hsitelink, $title, $subject, $preamble, $epreamble, $item['body'], $itemlink, true); } + + public function deleteForItem(int $itemUriId): void + { + $this->db->delete('notify', ['otype' => 'item', 'uri-id' => $itemUriId]); + $this->db->delete('notify', ['otype' => 'item', 'parent-uri-id' => $itemUriId]); + } } From db987999b521e0dfa4a1171c60b361dc0acb0038 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Sat, 26 Nov 2022 18:12:46 -0500 Subject: [PATCH 22/23] Return only one result in case of exact match in Api\Mastodon\Search --- src/Model/Contact.php | 3 +- src/Module/Api/Mastodon/Search.php | 73 ++++++++++++++++++++++++------ 2 files changed, 59 insertions(+), 17 deletions(-) diff --git a/src/Model/Contact.php b/src/Model/Contact.php index e0c3c1682..eabe378a4 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -3420,8 +3420,7 @@ class Contact ["(NOT `unsearchable` OR `nurl` IN (SELECT `nurl` FROM `owner-view` WHERE `publish` OR `net-publish`)) AND (`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?)", $search, $search, $search]); - $contacts = self::selectToArray([], $condition, $params); - return $contacts; + return self::selectToArray([], $condition, $params); } /** diff --git a/src/Module/Api/Mastodon/Search.php b/src/Module/Api/Mastodon/Search.php index fafdab0fd..fc9467308 100644 --- a/src/Module/Api/Mastodon/Search.php +++ b/src/Module/Api/Mastodon/Search.php @@ -26,6 +26,7 @@ use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; use Friendica\Model\Contact; +use Friendica\Model\Item; use Friendica\Model\Post; use Friendica\Model\Tag; use Friendica\Module\BaseApi; @@ -67,10 +68,24 @@ class Search extends BaseApi if (empty($request['type']) || ($request['type'] == 'accounts')) { $result['accounts'] = self::searchAccounts($uid, $request['q'], $request['resolve'], $limit, $request['offset'], $request['following']); + + if (!is_array($result['accounts'])) { + // Curbing the search if we got an exact result + $request['type'] = 'accounts'; + $result['accounts'] = [$result['accounts']]; + } } + if ((empty($request['type']) || ($request['type'] == 'statuses')) && (strpos($request['q'], '@') == false)) { $result['statuses'] = self::searchStatuses($uid, $request['q'], $request['account_id'], $request['max_id'], $request['min_id'], $limit, $request['offset']); + + if (!is_array($result['statuses'])) { + // Curbing the search if we got an exact result + $request['type'] = 'statuses'; + $result['statuses'] = [$result['statuses']]; + } } + if ((empty($request['type']) || ($request['type'] == 'hashtags')) && (strpos($request['q'], '@') == false)) { $result['hashtags'] = self::searchHashtags($request['q'], $request['exclude_unreviewed'], $limit, $request['offset'], $this->parameters['version']); } @@ -78,31 +93,59 @@ class Search extends BaseApi System::jsonExit($result); } + /** + * @param int $uid + * @param string $q + * @param bool $resolve + * @param int $limit + * @param int $offset + * @param bool $following + * @return array|\Friendica\Object\Api\Mastodon\Account Object if result is absolute (exact account match), list if not + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \Friendica\Network\HTTPException\NotFoundException + * @throws \ImagickException + */ private static function searchAccounts(int $uid, string $q, bool $resolve, int $limit, int $offset, bool $following) { - $accounts = []; - - if ((strrpos($q, '@') > 0) || Network::isValidHttpUrl($q)) { - $id = Contact::getIdForURL($q, 0, $resolve ? null : false); - - if (!empty($id)) { - $accounts[] = DI::mstdnAccount()->createFromContactId($id, $uid); - } + if ( + (strrpos($q, '@') > 0 || Network::isValidHttpUrl($q)) + && $id = Contact::getIdForURL($q, 0, $resolve ? null : false) + ) { + return DI::mstdnAccount()->createFromContactId($id, $uid); } - if (empty($accounts)) { - $contacts = Contact::searchByName($q, '', $following ? $uid : 0, $limit, $offset); - foreach ($contacts as $contact) { - $accounts[] = DI::mstdnAccount()->createFromContactId($contact['id'], $uid); - } - DBA::close($contacts); + $accounts = []; + foreach (Contact::searchByName($q, '', $following ? $uid : 0, $limit, $offset) as $contact) { + $accounts[] = DI::mstdnAccount()->createFromContactId($contact['id'], $uid); } return $accounts; } + /** + * @param int $uid + * @param string $q + * @param string $account_id + * @param int $max_id + * @param int $min_id + * @param int $limit + * @param int $offset + * @return array|\Friendica\Object\Api\Mastodon\Status Object is result is absolute (exact post match), list if not + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \Friendica\Network\HTTPException\NotFoundException + * @throws \ImagickException + */ private static function searchStatuses(int $uid, string $q, string $account_id, int $max_id, int $min_id, int $limit, int $offset) { + if (Network::isValidHttpUrl($q)) { + $q = Network::convertToIdn($q); + // If the user-specific search failed, we search and probe a public post + $item_id = Item::fetchByLink($q, $uid) ?: Item::fetchByLink($q); + if ($item_id && $item = Post::selectFirst(['uri-id'], ['id' => $item_id])) { + return DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid); + } + } + $params = ['order' => ['uri-id' => true], 'limit' => [$offset, $limit]]; if (substr($q, 0, 1) == '#') { @@ -148,7 +191,7 @@ class Search extends BaseApi return $statuses; } - private static function searchHashtags(string $q, bool $exclude_unreviewed, int $limit, int $offset, int $version) + private static function searchHashtags(string $q, bool $exclude_unreviewed, int $limit, int $offset, int $version): array { $q = ltrim($q, '#'); From 6d74d74a265366630896d1612c28f28b90f8d7ea Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 27 Nov 2022 20:47:08 +0000 Subject: [PATCH 23/23] Issue 12149: Show that a post was reshared --- src/Factory/Api/Mastodon/Status.php | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/Factory/Api/Mastodon/Status.php b/src/Factory/Api/Mastodon/Status.php index 54726815f..9d78e5892 100644 --- a/src/Factory/Api/Mastodon/Status.php +++ b/src/Factory/Api/Mastodon/Status.php @@ -76,16 +76,17 @@ class Status extends BaseFactory } /** - * @param int $uriId Uri-ID of the item - * @param int $uid Item user + * @param int $uriId Uri-ID of the item + * @param int $uid Item user + * @param bool $reblog Check for reblogged post * * @return \Friendica\Object\Api\Mastodon\Status * @throws HTTPException\InternalServerErrorException * @throws ImagickException|HTTPException\NotFoundException */ - public function createFromUriId(int $uriId, int $uid = 0): \Friendica\Object\Api\Mastodon\Status + public function createFromUriId(int $uriId, int $uid = 0, bool $reblog = true): \Friendica\Object\Api\Mastodon\Status { - $fields = ['uri-id', 'uid', 'author-id', 'author-uri-id', 'author-link', 'starred', 'app', 'title', 'body', 'raw-body', 'content-warning', 'question-id', + $fields = ['uri-id', 'uid', 'author-id', 'author-uri-id', 'author-link', 'causer-uri-id', 'post-reason', 'starred', 'app', 'title', 'body', 'raw-body', 'content-warning', 'question-id', 'created', 'network', 'thr-parent-id', 'parent-author-id', 'language', 'uri', 'plink', 'private', 'vid', 'gravity', 'featured', 'has-media', 'quote-uri-id']; $item = Post::selectFirst($fields, ['uri-id' => $uriId, 'uid' => [0, $uid]], ['order' => ['uid' => true]]); if (!$item) { @@ -95,7 +96,10 @@ class Status extends BaseFactory } throw new HTTPException\NotFoundException('Item with URI ID ' . $uriId . ' not found' . ($uid ? ' for user ' . $uid : '.')); } - $account = $this->mstdnAccountFactory->createFromUriId($item['author-uri-id'], $uid); + + $is_reshare = $reblog && !is_null($item['causer-uri-id']) && ($item['post-reason'] == Item::PR_ANNOUNCEMENT); + + $account = $this->mstdnAccountFactory->createFromUriId($is_reshare ? $item['causer-uri-id']:$item['author-uri-id'], $uid); $count_announce = Post::countPosts([ 'thr-parent-id' => $uriId, @@ -183,6 +187,10 @@ class Status extends BaseFactory $reshare = []; } + if ($is_reshare) { + $reshare = $this->createFromUriId($uriId, $uid, false)->toArray(); + } + return new \Friendica\Object\Api\Mastodon\Status($item, $account, $counts, $userAttributes, $sensitive, $application, $mentions, $tags, $card, $attachments, $reshare, $poll); }