diff --git a/doc/FAQ.md b/doc/FAQ.md index 118e05cbc4..0f9e115022 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -50,7 +50,7 @@ We recommend to talk to the admin(s) of the affected friendica server. (Admins, ### How can I upload images, files, links, videos and sound files to posts? You can upload images from your computer using the [editor](help/Text_editor). -An overview of all uploaded images is listed at *yourpage.com/photos/profilename*. +An overview of all uploaded images is listed at *yourpage.com/profile/profilename/photos*. On that page, you can also upload images directly and choose if your contacts will receive a message about this upload. Generally, you can attach any kind of file to a post. diff --git a/doc/de/FAQ.md b/doc/de/FAQ.md index e3e4ac672f..0e5f9c42ff 100644 --- a/doc/de/FAQ.md +++ b/doc/de/FAQ.md @@ -69,7 +69,7 @@ Andere erlauben nur kostenpflichtige Zertifikate als eigenes Angebot bzw. von an ### Wie kann ich Bilder, Dateien, Links, Video und Audio in Beiträge einfügen? Bilder können direkt im [Beitragseditor](help/Text_editor) vom Computer hochgeladen werden. -Eine Übersicht aller Bilder, die auf Deinem Server liegen, findest Du unter deineSeite.de/photos/profilname. +Eine Übersicht aller Bilder, die auf Deinem Server liegen, findest Du unter deineSeite.de/profile/profilname/photos. Dort kannst Du auch direkt Bilder hochladen und festlegen, ob Deine Kontakte eine Nachricht über das neue Bild bekommen. Alle Arten von Dateien können grundsätzlich als Anhang in Friendica hochgeladen werden. diff --git a/mod/photos.php b/mod/photos.php index 3923bdfc89..e5a24d22a9 100644 --- a/mod/photos.php +++ b/mod/photos.php @@ -284,8 +284,7 @@ function photos_post(App $a) DI::baseUrl()->redirect('photos/' . DI::args()->getArgv()[1] . '/image/' . DI::args()->getArgv()[3]); } - DI::baseUrl()->redirect('photos/' . DI::args()->getArgv()[1]); - return; // NOTREACHED + DI::baseUrl()->redirect('profile/' . DI::args()->getArgv()[1] . '/photos'); } } @@ -778,7 +777,6 @@ function photos_post(App $a) function photos_content(App $a) { // URLs: - // photos/name // photos/name/upload // photos/name/upload/xxxxx (xxxxx is album name) // photos/name/album/xxxxx @@ -905,7 +903,7 @@ function photos_content(App $a) $uploader = ''; - $ret = ['post_url' => 'photos/' . $user['nickname'], + $ret = ['post_url' => 'profile/' . $user['nickname'] . '/photos', 'addon_text' => $uploader, 'default_upload' => true]; @@ -1522,68 +1520,4 @@ function photos_content(App $a) return $o; } - - // Default - show recent photos with upload link (if applicable) - //$o = ''; - $total = 0; - $r = DBA::toArray(DBA::p("SELECT `resource-id`, max(`scale`) AS `scale` FROM `photo` WHERE `uid` = ? AND `photo-type` = ? - $sql_extra GROUP BY `resource-id`", - $user['uid'], - Photo::DEFAULT, - )); - if (DBA::isResult($r)) { - $total = count($r); - } - - $pager = new Pager(DI::l10n(), DI::args()->getQueryString(), 20); - - $r = DBA::toArray(DBA::p("SELECT `resource-id`, ANY_VALUE(`id`) AS `id`, ANY_VALUE(`filename`) AS `filename`, - ANY_VALUE(`type`) AS `type`, ANY_VALUE(`album`) AS `album`, max(`scale`) AS `scale`, - ANY_VALUE(`created`) AS `created` FROM `photo` - WHERE `uid` = ? AND `photo-type` = ? - $sql_extra GROUP BY `resource-id` ORDER BY `created` DESC LIMIT ? , ?", - $user['uid'], - Photo::DEFAULT, - $pager->getStart(), - $pager->getItemsPerPage() - )); - - $photos = []; - if (DBA::isResult($r)) { - // "Twist" is only used for the duepunto theme with style "slackr" - $twist = false; - foreach ($r as $rr) { - $twist = !$twist; - $ext = $phototypes[$rr['type']]; - - $alt_e = $rr['filename']; - $name_e = $rr['album']; - - $photos[] = [ - 'id' => $rr['id'], - 'twist' => ' ' . ($twist ? 'rotleft' : 'rotright') . rand(2,4), - 'link' => 'photos/' . $user['nickname'] . '/image/' . $rr['resource-id'], - 'title' => DI::l10n()->t('View Photo'), - 'src' => 'photo/' . $rr['resource-id'] . '-' . ((($rr['scale']) == 6) ? 4 : $rr['scale']) . '.' . $ext, - 'alt' => $alt_e, - 'album' => [ - 'link' => 'photos/' . $user['nickname'] . '/album/' . bin2hex($rr['album']), - 'name' => $name_e, - 'alt' => DI::l10n()->t('View Album'), - ], - - ]; - } - } - - $tpl = Renderer::getMarkupTemplate('photos_recent.tpl'); - $o .= Renderer::replaceMacros($tpl, [ - '$title' => DI::l10n()->t('Recent Photos'), - '$can_post' => $can_post, - '$upload' => [DI::l10n()->t('Upload New Photos'), 'photos/' . $user['nickname'] . '/upload'], - '$photos' => $photos, - '$paginate' => $pager->renderFull($total), - ]); - - return $o; } diff --git a/src/Content/Item.php b/src/Content/Item.php index 0d4c3c180b..4fb1f85d62 100644 --- a/src/Content/Item.php +++ b/src/Content/Item.php @@ -361,7 +361,7 @@ class Item if ($sparkle) { $status_link = $profile_link . '/status'; - $photos_link = str_replace('/profile/', '/photos/', $profile_link); + $photos_link = $profile_link . '/photos'; $profile_link = $profile_link . '/profile'; } @@ -729,7 +729,7 @@ class Item 'message_id' => $shared['uri'], 'comment' => $item['body'], 'shared' => $shared['body'], - ]; + ]; } } @@ -737,7 +737,7 @@ class Item } /** - * Add a link to a shared post at the end of the post + * Add a link to a shared post at the end of the post * * @param string $body * @param integer $quote_uri_id diff --git a/src/Content/Nav.php b/src/Content/Nav.php index cb2edb4eb6..8ebb412db7 100644 --- a/src/Content/Nav.php +++ b/src/Content/Nav.php @@ -192,7 +192,7 @@ class Nav // user menu $nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname(), DI::l10n()->t('Status'), '', DI::l10n()->t('Your posts and conversations')]; $nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname() . '/profile', DI::l10n()->t('Profile'), '', DI::l10n()->t('Your profile page')]; - $nav['usermenu'][] = ['photos/' . $a->getLoggedInUserNickname(), DI::l10n()->t('Photos'), '', DI::l10n()->t('Your photos')]; + $nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname() . '/photos', DI::l10n()->t('Photos'), '', DI::l10n()->t('Your photos')]; $nav['usermenu'][] = ['profile/' . $a->getLoggedInUserNickname() . '/media', DI::l10n()->t('Media'), '', DI::l10n()->t('Your postings with media')]; $nav['usermenu'][] = ['calendar/', DI::l10n()->t('Calendar'), '', DI::l10n()->t('Your calendar')]; $nav['usermenu'][] = ['notes/', DI::l10n()->t('Personal notes'), '', DI::l10n()->t('Your personal notes')]; diff --git a/src/Model/Contact.php b/src/Model/Contact.php index cd4e52acd5..ce392217dd 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -1158,7 +1158,7 @@ class Contact if ($sparkle) { $status_link = $profile_link . '/status'; - $photos_link = str_replace('/profile/', '/photos/', $profile_link); + $photos_link = $profile_link . '/photos'; $profile_link = $profile_link . '/profile'; } diff --git a/src/Module/BaseProfile.php b/src/Module/BaseProfile.php index 16ba224c9c..56577ef33a 100644 --- a/src/Module/BaseProfile.php +++ b/src/Module/BaseProfile.php @@ -62,7 +62,7 @@ class BaseProfile extends BaseModule ], [ 'label' => DI::l10n()->t('Photos'), - 'url' => DI::baseUrl() . '/photos/' . $nickname, + 'url' => $baseProfileUrl . '/photos', 'sel' => $current == 'photos' ? 'active' : '', 'title' => DI::l10n()->t('Photo Albums'), 'id' => 'photo-tab', @@ -78,7 +78,7 @@ class BaseProfile extends BaseModule ], ]; - // the calendar link for the full featured events calendar + // the calendar link for the full-featured events calendar if ($is_owner && $a->getThemeInfoValue('events_in_profile')) { $tabs[] = [ 'label' => DI::l10n()->t('Calendar'), diff --git a/src/Module/Profile/Photos/Index.php b/src/Module/Profile/Photos/Index.php new file mode 100644 index 0000000000..82803ac570 --- /dev/null +++ b/src/Module/Profile/Photos/Index.php @@ -0,0 +1,196 @@ +. + * + */ + +namespace Friendica\Module\Profile\Photos; + +use Friendica\App; +use Friendica\Content\Pager; +use Friendica\Content\Widget; +use Friendica\Core\Config\Capability\IManageConfigValues; +use Friendica\Core\L10n; +use Friendica\Core\Renderer; +use Friendica\Core\Session\Capability\IHandleUserSessions; +use Friendica\Database\Database; +use Friendica\Model\Contact; +use Friendica\Model\Photo; +use Friendica\Model\Profile; +use Friendica\Model\User; +use Friendica\Module\Response; +use Friendica\Network\HTTPException; +use Friendica\Security\Security; +use Friendica\Util\Images; +use Friendica\Util\Profiler; +use Psr\Log\LoggerInterface; + +class Index extends \Friendica\Module\BaseProfile +{ + /** @var IHandleUserSessions */ + private $session; + /** @var App\Page */ + private $page; + /** @var IManageConfigValues */ + private $config; + /** @var App */ + private $app; + /** @var Database */ + private $database; + + public function __construct(Database $database, App $app, IManageConfigValues $config, App\Page $page, IHandleUserSessions $session, 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->session = $session; + $this->page = $page; + $this->config = $config; + $this->app = $app; + $this->database = $database; + } + + protected function content(array $request = []): string + { + parent::content($request); + + if ($this->config->get('system', 'block_public') && !$this->session->isAuthenticated()) { + throw new HttpException\ForbiddenException($this->t('Public access denied.')); + } + + $owner = User::getOwnerDataByNick($this->parameters['nickname']); + if (!isset($owner['account_removed']) || $owner['account_removed']) { + throw new HTTPException\NotFoundException($this->t('User not found.')); + } + + $owner_uid = $owner['uid']; + $is_owner = $this->session->getLocalUserId() && ($this->session->getLocalUserId() == $owner_uid); + + $remote_contact = false; + if ($this->session->getRemoteContactID($owner_uid)) { + $contact_id = $this->session->getRemoteContactID($owner_uid); + + $contact = Contact::getContactForUser($contact_id, $owner_uid, ['blocked', 'pending']); + $remote_contact = $contact && !$contact['blocked'] && !$contact['pending']; + } + + if ($owner['hidewall'] && !$is_owner && !$remote_contact) { + throw new HttpException\ForbiddenException($this->t('Access to this item is restricted.')); + } + + $this->session->set('photo_return', $this->args->getCommand()); + + $sql_extra = Security::getPermissionsSQLByUserId($owner_uid); + + $photo = $this->database->toArray($this->database->p( + "SELECT COUNT(DISTINCT `resource-id`) AS `count` + FROM `photo` + WHERE `uid` = ? + AND `photo-type` = ? + $sql_extra", + $owner['uid'], + Photo::DEFAULT, + )); + $total = $photo[0]['count']; + + $pager = new Pager($this->l10n, $this->args->getQueryString(), 20); + + $photos = $this->database->toArray($this->database->p( + "SELECT + `resource-id`, + ANY_VALUE(`id`) AS `id`, + ANY_VALUE(`filename`) AS `filename`, + ANY_VALUE(`type`) AS `type`, + ANY_VALUE(`album`) AS `album`, + max(`scale`) AS `scale`, + ANY_VALUE(`created`) AS `created` + FROM `photo` + WHERE `uid` = ? + AND `photo-type` = ? + $sql_extra + GROUP BY `resource-id` + ORDER BY `created` DESC + LIMIT ? , ?", + $owner['uid'], + Photo::DEFAULT, + $pager->getStart(), + $pager->getItemsPerPage() + )); + + $phototypes = Images::supportedTypes(); + + $photos = array_map(function ($photo) use ($owner, $phototypes) { + return [ + 'id' => $photo['id'], + 'link' => 'photos/' . $owner['nickname'] . '/image/' . $photo['resource-id'], + 'title' => $this->t('View Photo'), + 'src' => 'photo/' . $photo['resource-id'] . '-' . ((($photo['scale']) == 6) ? 4 : $photo['scale']) . '.' . $phototypes[$photo['type']], + 'alt' => $photo['filename'], + 'album' => [ + 'link' => 'photos/' . $owner['nickname'] . '/album/' . bin2hex($photo['album']), + 'name' => $photo['album'], + 'alt' => $this->t('View Album'), + ], + ]; + }, $photos); + + $tpl = Renderer::getMarkupTemplate('photos_head.tpl'); + $this->page['htmlhead'] .= Renderer::replaceMacros($tpl, [ + '$ispublic' => $this->t('everybody') + ]); + + if ($albums = Photo::getAlbums($owner['uid'])) { + $albums = array_map(function ($album) use ($owner) { + return [ + 'text' => $album['album'], + 'total' => $album['total'], + 'url' => 'photos/' . $owner['nickname'] . '/album/' . bin2hex($album['album']), + 'urlencode' => urlencode($album['album']), + 'bin2hex' => bin2hex($album['album']) + ]; + }, $albums); + + $photo_albums_widget = Renderer::replaceMacros(Renderer::getMarkupTemplate('photo_albums.tpl'), [ + '$nick' => $owner['nickname'], + '$title' => $this->t('Photo Albums'), + '$recent' => $this->t('Recent Photos'), + '$albums' => $albums, + '$upload' => [$this->t('Upload New Photos'), 'photos/' . $owner['nickname'] . '/upload'], + '$can_post' => $this->session->getLocalUserId() && $owner['uid'] == $this->session->getLocalUserId(), + ]); + } + + $this->page['aside'] .= Widget\VCard::getHTML($owner); + + if (!empty($photo_albums_widget)) { + $this->page['aside'] .= $photo_albums_widget; + } + + $o = self::getTabsHTML($this->app, 'photos', $is_owner, $owner['nickname'], Profile::getByUID($owner['uid'])['hide-friends'] ?? false); + + $tpl = Renderer::getMarkupTemplate('photos_recent.tpl'); + $o .= Renderer::replaceMacros($tpl, [ + '$title' => $this->t('Recent Photos'), + '$can_post' => $is_owner || $remote_contact && $owner['page-flags'] == User::PAGE_FLAGS_COMMUNITY, + '$upload' => [$this->t('Upload New Photos'), 'photos/' . $owner['nickname'] . '/upload'], + '$photos' => $photos, + '$paginate' => $pager->renderFull($total), + ]); + + return $o; + } +} diff --git a/src/Module/Settings/Profile/Index.php b/src/Module/Settings/Profile/Index.php index 52f61efd48..76930b7263 100644 --- a/src/Module/Settings/Profile/Index.php +++ b/src/Module/Settings/Profile/Index.php @@ -230,7 +230,7 @@ class Index extends BaseSettings '$banner' => DI::l10n()->t('Edit Profile Details'), '$submit' => DI::l10n()->t('Submit'), '$profpic' => DI::l10n()->t('Change Profile Photo'), - '$profpiclink' => '/photos/' . $profile['nickname'], + '$profpiclink' => '/profile/' . $profile['nickname'] . '/photos', '$viewprof' => DI::l10n()->t('View Profile'), '$lbl_personal_section' => DI::l10n()->t('Personal'), diff --git a/src/Module/Settings/Profile/Photo/Index.php b/src/Module/Settings/Profile/Photo/Index.php index 3553a9f401..d58576e757 100644 --- a/src/Module/Settings/Profile/Photo/Index.php +++ b/src/Module/Settings/Profile/Photo/Index.php @@ -132,7 +132,7 @@ class Index extends BaseSettings DI::l10n()->t('or'), ($newuser) ? '' . DI::l10n()->t('skip this step') . '' - : '' + : '' . DI::l10n()->t('select a photo from your photo albums') . '' ), ]); diff --git a/static/routes.config.php b/static/routes.config.php index 9adc8ec1dd..13c8737187 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -36,6 +36,7 @@ $profileRoutes = [ '/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]], @@ -553,6 +554,10 @@ return [ '/{type}/{customsize:\d+}/{nickname_ext}' => [Module\Photo::class, [R::GET]], ], + // Kept for backwards-compatibility + // @TODO remove by version 2023.12 + '/photos/{nickname}' => [Module\Profile\Photos\Index::class, [R::GET]], + '/ping' => [Module\Notifications\Ping::class, [R::GET]], '/post' => [ diff --git a/view/templates/photo_albums.tpl b/view/templates/photo_albums.tpl index 86b468e06d..f5a13d714e 100644 --- a/view/templates/photo_albums.tpl +++ b/view/templates/photo_albums.tpl @@ -2,14 +2,14 @@

{{$title}}