Create /profile/{nickname}/photos route

- Create new Profile\Photos\Index module class
- Update most references of the legacy /photos/{nickname} route
This commit is contained in:
Hypolite Petovan 2022-11-20 17:15:07 -05:00
parent 6dcc964ed8
commit 77e87da441
16 changed files with 225 additions and 88 deletions

View file

@ -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.

View file

@ -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 <i>deineSeite.de/photos/profilname</i>.
Eine Übersicht aller Bilder, die auf Deinem Server liegen, findest Du unter <i>deineSeite.de/profile/profilname/photos</i>.
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.

View file

@ -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;
}

View file

@ -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';
}

View file

@ -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')];

View file

@ -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';
}

View file

@ -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'),

View file

@ -0,0 +1,196 @@
<?php
/**
* @copyright Copyright (C) 2010-2022, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/
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;
}
}

View file

@ -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'),

View file

@ -132,7 +132,7 @@ class Index extends BaseSettings
DI::l10n()->t('or'),
($newuser) ?
'<a href="' . DI::baseUrl() . '">' . DI::l10n()->t('skip this step') . '</a>'
: '<a href="' . DI::baseUrl() . '/photos/' . DI::app()->getLoggedInUserNickname() . '">'
: '<a href="' . DI::baseUrl() . '/profile/' . DI::app()->getLoggedInUserNickname() . '/photos">'
. DI::l10n()->t('select a photo from your photo albums') . '</a>'
),
]);

View file

@ -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' => [

View file

@ -2,14 +2,14 @@
<h3>{{$title}}</h3>
<ul role="menubar" class="sidebar-photos-albums-ul">
<li role="menuitem" class="sidebar-photos-albums-li">
<a href="{{$baseurl}}/photos/{{$nick}}" class="sidebar-photos-albums-element" title="{{$title}}">{{$recent}}</a>
<a href="profile/{{$nick}}/photos" class="sidebar-photos-albums-element" title="{{$title}}">{{$recent}}</a>
</li>
{{if $albums}}
{{foreach $albums as $al}}
{{if $al.text}}
<li role="menuitem" class="sidebar-photos-albums-li">
<a href="{{$baseurl}}/photos/{{$nick}}/album/{{$al.bin2hex}}" class="sidebar-photos-albums-element">
<a href="photos/{{$nick}}/album/{{$al.bin2hex}}" class="sidebar-photos-albums-element">
<span class="badge pull-right">{{$al.total}}</span>{{$al.text}}
</a>
</li>

View file

@ -3,7 +3,7 @@
<div id="photos-usage-message">{{$usage}}</div>
<form action="photos/{{$nickname}}" enctype="multipart/form-data" method="post" name="photos-upload-form" id="photos-upload-form">
<form action="profile/{{$nickname}}/photos" enctype="multipart/form-data" method="post" name="photos-upload-form" id="photos-upload-form">
<div id="photos-upload-new-wrapper">
<div id="photos-upload-newalbum-div">
<label id="photos-upload-newalbum-text" for="photos-upload-newalbum">{{$newalbum}}</label>
@ -47,4 +47,3 @@
<div class="photos-upload-end"></div>
</form>

View file

@ -15,14 +15,14 @@
<ul role="menubar" class="sidebar-photos-albums-ul clear">
<li role="menuitem" class="sidebar-photos-albums-li">
<a href="{{$baseurl}}/photos/{{$nick}}" class="sidebar-photos-albums-element" title="{{$title}}">{{$recent}}</a>
<a href="profile/{{$nick}}/photos" class="sidebar-photos-albums-element" title="{{$title}}">{{$recent}}</a>
</li>
{{if $albums}}
{{foreach $albums as $al}}
{{if $al.text}}
<li role="menuitem" class="sidebar-photos-albums-li">
<a href="{{$baseurl}}/photos/{{$nick}}/album/{{$al.bin2hex}}" class="sidebar-photos-albums-element">
<a href="photos/{{$nick}}/album/{{$al.bin2hex}}" class="sidebar-photos-albums-element">
<span class="badge pull-right">{{$al.total}}</span>{{$al.text}}
</a>
</li>

View file

@ -4,7 +4,7 @@
<div id="photos-usage-message">{{$usage}}</div>
<form action="photos/{{$nickname}}" enctype="multipart/form-data" method="post" name="photos-upload-form" id="photos-upload-form">
<form action="profile/{{$nickname}}/photos" enctype="multipart/form-data" method="post" name="photos-upload-form" id="photos-upload-form">
<div id="photos-upload-div" class="form-group">
<label id="photos-upload-text" for="photos-upload-newalbum">{{$newalbum}}</label>

View file

@ -236,6 +236,9 @@ function frio_remote_nav(App $a, array &$nav_info)
// user menu
$nav_info['nav']['usermenu'][] = [$server_url . '/profile/' . $remoteUser['nick'], DI::l10n()->t('Status'), '', DI::l10n()->t('Your posts and conversations')];
$nav_info['nav']['usermenu'][] = [$server_url . '/profile/' . $remoteUser['nick'] . '/profile', DI::l10n()->t('Profile'), '', DI::l10n()->t('Your profile page')];
// Kept for backwards-compatibility reasons, the remote server may not have updated to version 2022.12 yet
// @TODO Switch with the new routes by version 2023.12
//$nav_info['nav']['usermenu'][] = [$server_url . '/profile/' . $remoteUser['nick'] . '/photos', DI::l10n()->t('Photos'), '', DI::l10n()->t('Your photos')];
$nav_info['nav']['usermenu'][] = [$server_url . '/photos/' . $remoteUser['nick'], DI::l10n()->t('Photos'), '', DI::l10n()->t('Your photos')];
$nav_info['nav']['usermenu'][] = [$server_url . '/profile/' . $remoteUser['nick'] . '/media', DI::l10n()->t('Media'), '', DI::l10n()->t('Your postings with media')];
$nav_info['nav']['usermenu'][] = [$server_url . '/calendar/', DI::l10n()->t('Calendar'), '', DI::l10n()->t('Your calendar')];