Merge pull request #12179 from nupplaphil/mod/display

Move mod/(update_)display.php to src\Module
This commit is contained in:
Hypolite Petovan 2022-11-15 15:14:17 -05:00 committed by GitHub
commit 403b025b5d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 590 additions and 430 deletions

View file

@ -1,368 +0,0 @@
<?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/>.
*
*/
use Friendica\App;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Widget;
use Friendica\Core\Logger;
use Friendica\Core\Renderer;
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\User;
use Friendica\Module\ActivityPub\Objects;
use Friendica\Module\Response;
use Friendica\Network\HTTPException;
use Friendica\Protocol\ActivityPub;
use Friendica\Protocol\DFRN;
use Friendica\Protocol\Diaspora;
use Friendica\Util\DateTimeFormat;
function display_init(App $a)
{
if (ActivityPub::isRequest()) {
(new Objects(DI::l10n(), DI::baseUrl(), DI::args(), DI::logger(), DI::profiler(), DI::apiResponse(), $_SERVER, ['guid' => DI::args()->getArgv()[1] ?? null]))->run();
}
if (DI::config()->get('system', 'block_public') && !DI::userSession()->isAuthenticated()) {
return;
}
$nick = ((DI::args()->getArgc() > 1) ? DI::args()->getArgv()[1] : '');
$item = null;
$item_user = DI::userSession()->getLocalUserId();
$fields = ['uri-id', 'parent-uri-id', 'author-id', 'author-link', 'body', 'uid', 'guid', 'gravity'];
// If there is only one parameter, then check if this parameter could be a guid
if (DI::args()->getArgc() == 2) {
$nick = '';
// Does the local user have this item?
if (DI::userSession()->getLocalUserId()) {
$item = Post::selectFirstForUser(DI::userSession()->getLocalUserId(), $fields, ['guid' => DI::args()->getArgv()[1], 'uid' => DI::userSession()->getLocalUserId()]);
if (DBA::isResult($item)) {
$nick = $a->getLoggedInUserNickname();
}
}
// Is this item private but could be visible to the remove visitor?
if (!DBA::isResult($item) && DI::userSession()->getRemoteUserId()) {
$item = Post::selectFirst($fields, ['guid' => DI::args()->getArgv()[1], 'private' => Item::PRIVATE, 'origin' => true]);
if (DBA::isResult($item)) {
if (!Contact::isFollower(DI::userSession()->getRemoteUserId(), $item['uid'])) {
$item = null;
} else {
$item_user = $item['uid'];
}
}
}
// Is it an item with uid=0?
if (!DBA::isResult($item)) {
$item = Post::selectFirstForUser(DI::userSession()->getLocalUserId(), $fields, ['guid' => DI::args()->getArgv()[1], 'private' => [Item::PUBLIC, Item::UNLISTED], 'uid' => 0]);
}
} elseif (DI::args()->getArgc() >= 3 && $nick == 'feed-item') {
$uri_id = DI::args()->getArgv()[2];
if (substr($uri_id, -5) == '.atom') {
$uri_id = substr($uri_id, 0, -5);
}
$item = Post::selectFirstForUser(DI::userSession()->getLocalUserId(), $fields, ['uri-id' => $uri_id, 'private' => [Item::PUBLIC, Item::UNLISTED], 'uid' => 0]);
}
if (!DBA::isResult($item)) {
return;
}
if (DI::args()->getArgc() >= 3 && $nick == 'feed-item') {
displayShowFeed($item['uri-id'], $item['uid'], DI::args()->getArgc() > 3 && DI::args()->getArgv()[3] == 'conversation.atom');
}
if (!empty($_SERVER['HTTP_ACCEPT']) && strstr($_SERVER['HTTP_ACCEPT'], 'application/atom+xml')) {
Logger::debug('Directly serving XML', ['uri-id' => $item['uri-id']]);
displayShowFeed($item['uri-id'], $item['uid'], false);
}
if ($item['gravity'] != Item::GRAVITY_PARENT) {
$parent = Post::selectFirstForUser($item_user, $fields, ['uid' => [0, $item_user], 'uri-id' => $item['parent-uri-id']], ['order' => ['uid' => true]]);
$item = $parent ?: $item;
}
$author = display_fetchauthor($item);
if (\Friendica\Util\Network::isLocalLink($author['url'])) {
\Friendica\Model\Profile::load(DI::app(), $author['nick'], false);
} else {
DI::page()['aside'] = Widget\VCard::getHTML($author);
}
$a->setProfileOwner($item['uid']);
}
function display_fetchauthor($item)
{
$shared = DI::contentItem()->getSharedPost($item, ['author-link']);
if (!empty($shared) && empty($shared['comment'])) {
$contact = Contact::getByURLForUser($shared['post']['author-link'], DI::userSession()->getLocalUserId());
}
if (empty($contact)) {
$contact = Contact::getById($item['author-id']);
}
return $contact;
}
function display_content(App $a, $update = false, $update_uid = 0)
{
if (DI::config()->get('system','block_public') && !DI::userSession()->isAuthenticated()) {
throw new HTTPException\ForbiddenException(DI::l10n()->t('Public access denied.'));
}
$o = '';
$item = null;
$force = (bool)($_REQUEST['force'] ?? false);
if ($update) {
$uri_id = $_REQUEST['uri_id'];
$item = Post::selectFirst(['uid', 'parent-uri-id'], ['uri-id' => $uri_id, 'uid' => [0, $update_uid]], ['order' => ['uid' => true]]);
if (!empty($item)) {
if ($item['uid'] != 0) {
$a->setProfileOwner($item['uid']);
} else {
$a->setProfileOwner($update_uid);
}
$parent_uri_id = $item['parent-uri-id'];
}
if (empty($_REQUEST['force'])) {
$browser_update = intval(DI::pConfig()->get($update_uid, 'system', 'update_interval'));
if (!empty($browser_update)) {
$update_date = date(DateTimeFormat::MYSQL, time() - ($browser_update / 500));
if (!Post::exists(["`parent-uri-id` = ? AND `uid` IN (?, ?) AND `received` > ?", $parent_uri_id, 0, $update_uid, $update_date])) {
Logger::debug('No updated content', ['uri-id' => $uri_id, 'uid' => $update_uid, 'updated' => $update_date]);
return '';
} else {
Logger::debug('Updated content found', ['uri-id' => $uri_id, 'uid' => $update_uid, 'updated' => $update_date]);
}
}
} else {
Logger::debug('Forced content update', ['uri-id' => $uri_id, 'uid' => $update_uid]);
}
} else {
$uri_id = ((DI::args()->getArgc() > 2) ? DI::args()->getArgv()[2] : 0);
$parent_uri_id = $uri_id;
if (DI::args()->getArgc() == 2) {
$fields = ['uri-id', 'parent-uri-id', 'uid'];
if (DI::userSession()->getLocalUserId()) {
$condition = ['guid' => DI::args()->getArgv()[1], 'uid' => [0, DI::userSession()->getLocalUserId()]];
$item = Post::selectFirstForUser(DI::userSession()->getLocalUserId(), $fields, $condition, ['order' => ['uid' => true]]);
if (DBA::isResult($item)) {
$uri_id = $item['uri-id'];
$parent_uri_id = $item['parent-uri-id'];
}
}
if (($parent_uri_id == 0) && DI::userSession()->getRemoteUserId()) {
$item = Post::selectFirst($fields, ['guid' => DI::args()->getArgv()[1], 'private' => Item::PRIVATE, 'origin' => true]);
if (DBA::isResult($item) && Contact::isFollower(DI::userSession()->getRemoteUserId(), $item['uid'])) {
$uri_id = $item['uri-id'];
$parent_uri_id = $item['parent-uri-id'];
}
}
if ($parent_uri_id == 0) {
$condition = ['private' => [Item::PUBLIC, Item::UNLISTED], 'guid' => DI::args()->getArgv()[1], 'uid' => 0];
$item = Post::selectFirstForUser(DI::userSession()->getLocalUserId(), $fields, $condition);
if (DBA::isResult($item)) {
$uri_id = $item['uri-id'];
$parent_uri_id = $item['parent-uri-id'];
}
}
}
}
if (empty($item)) {
throw new HTTPException\NotFoundException(DI::l10n()->t('The requested item doesn\'t exist or has been deleted.'));
}
if (!DI::pConfig()->get(DI::userSession()->getLocalUserId(), 'system', 'detailed_notif')) {
DI::notification()->setAllSeenForUser(DI::userSession()->getLocalUserId(), ['parent-uri-id' => $item['parent-uri-id']]);
DI::notify()->setAllSeenForUser(DI::userSession()->getLocalUserId(), ['parent-uri-id' => $item['parent-uri-id']]);
}
// We are displaying an "alternate" link if that post was public. See issue 2864
$is_public = Post::exists(['uri-id' => $uri_id, 'private' => [Item::PUBLIC, Item::UNLISTED]]);
if ($is_public) {
// For the atom feed the nickname doesn't matter at all, we only need the item id.
$alternate = DI::baseUrl().'/display/feed-item/'.$uri_id.'.atom';
$conversation = DI::baseUrl().'/display/feed-item/' . $parent_uri_id . '/conversation.atom';
} else {
$alternate = '';
$conversation = '';
}
DI::page()['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('display-head.tpl'),
['$alternate' => $alternate,
'$conversation' => $conversation]);
$is_remote_contact = false;
$item_uid = DI::userSession()->getLocalUserId();
$page_uid = 0;
$parent = null;
if (!DI::userSession()->getLocalUserId() && !empty($parent_uri_id)) {
$parent = Post::selectFirst(['uid'], ['uri-id' => $parent_uri_id, 'wall' => true]);
}
if (DBA::isResult($parent)) {
$page_uid = $page_uid ?? 0 ?: $parent['uid'];
$is_remote_contact = DI::userSession()->getRemoteContactID($page_uid);
if ($is_remote_contact) {
$item_uid = $parent['uid'];
}
} else {
$page_uid = $item['uid'];
}
if (!empty($page_uid) && ($page_uid != DI::userSession()->getLocalUserId())) {
$page_user = User::getById($page_uid);
}
$is_owner = DI::userSession()->getLocalUserId() && (in_array($page_uid, [DI::userSession()->getLocalUserId(), 0]));
if (!empty($page_user['hidewall']) && !$is_owner && !$is_remote_contact) {
throw new HTTPException\ForbiddenException(DI::l10n()->t('Access to this profile has been restricted.'));
}
// We need the editor here to be able to reshare an item.
if ($is_owner && !$update) {
$o .= DI::conversation()->statusEditor([], 0, true);
}
$sql_extra = Item::getPermissionsSQLByUserId($page_uid);
if (DI::userSession()->getLocalUserId() && (DI::userSession()->getLocalUserId() == $page_uid)) {
$condition = ['parent-uri-id' => $parent_uri_id, 'uid' => DI::userSession()->getLocalUserId(), 'unseen' => true];
$unseen = Post::exists($condition);
} else {
$unseen = false;
}
if ($update && !$unseen && !$force) {
return '';
}
$condition = ["`uri-id` = ? AND `uid` IN (0, ?) " . $sql_extra, $uri_id, $item_uid];
$fields = ['parent-uri-id', 'body', 'title', 'author-name', 'author-avatar', 'plink', 'author-id', 'owner-id', 'contact-id'];
$item = Post::selectFirstForUser($page_uid, $fields, $condition);
if (!DBA::isResult($item)) {
throw new HTTPException\NotFoundException(DI::l10n()->t('The requested item doesn\'t exist or has been deleted.'));
}
$item['uri-id'] = $item['parent-uri-id'];
if ($unseen) {
$condition = ['parent-uri-id' => $parent_uri_id, 'uid' => DI::userSession()->getLocalUserId(), 'unseen' => true];
Item::update(['unseen' => false], $condition);
}
if (!$update && DI::userSession()->getLocalUserId()) {
$o .= "<script> var netargs = '?uri_id=" . $item['uri-id'] . "'; </script>";
}
$o .= DI::conversation()->create([$item], 'display', $update_uid, false, 'commented', $item_uid);
// Preparing the meta header
$description = trim(BBCode::toPlaintext($item['body']));
$title = trim(BBCode::toPlaintext($item['title'] ?? ''));
$author_name = $item['author-name'];
$image = DI::baseUrl()->remove($item['author-avatar']);
if ($title == '') {
$title = $author_name;
}
// Limit the description to 160 characters
if (strlen($description) > 160) {
$description = substr($description, 0, 157) . '...';
}
$description = htmlspecialchars($description, ENT_COMPAT, 'UTF-8', true); // allow double encoding here
$title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8', true); // allow double encoding here
$author_name = htmlspecialchars($author_name, ENT_COMPAT, 'UTF-8', true); // allow double encoding here
$page = DI::page();
if (DBA::exists('contact', ['unsearchable' => true, 'id' => [$item['contact-id'], $item['author-id'], $item['owner-id']]])) {
$page['htmlhead'] .= '<meta content="noindex, noarchive" name="robots" />' . "\n";
}
DI::page()['htmlhead'] .= '<meta name="author" content="'.$author_name.'" />'."\n";
$page['htmlhead'] .= '<meta name="title" content="'.$title.'" />'."\n";
$page['htmlhead'] .= '<meta name="fulltitle" content="'.$title.'" />'."\n";
$page['htmlhead'] .= '<meta name="description" content="'.$description.'" />'."\n";
// Schema.org microdata
$page['htmlhead'] .= '<meta itemprop="name" content="'.$title.'" />'."\n";
$page['htmlhead'] .= '<meta itemprop="description" content="'.$description.'" />'."\n";
$page['htmlhead'] .= '<meta itemprop="image" content="'.$image.'" />'."\n";
$page['htmlhead'] .= '<meta itemprop="author" content="'.$author_name.'" />'."\n";
// Twitter cards
$page['htmlhead'] .= '<meta name="twitter:card" content="summary" />'."\n";
$page['htmlhead'] .= '<meta name="twitter:title" content="'.$title.'" />'."\n";
$page['htmlhead'] .= '<meta name="twitter:description" content="'.$description.'" />'."\n";
$page['htmlhead'] .= '<meta name="twitter:image" content="'.DI::baseUrl().'/'.$image.'" />'."\n";
$page['htmlhead'] .= '<meta name="twitter:url" content="'.$item["plink"].'" />'."\n";
// Dublin Core
$page['htmlhead'] .= '<meta name="DC.title" content="'.$title.'" />'."\n";
$page['htmlhead'] .= '<meta name="DC.description" content="'.$description.'" />'."\n";
// Open Graph
$page['htmlhead'] .= '<meta property="og:type" content="website" />'."\n";
$page['htmlhead'] .= '<meta property="og:title" content="'.$title.'" />'."\n";
$page['htmlhead'] .= '<meta property="og:image" content="'.DI::baseUrl().'/'.$image.'" />'."\n";
$page['htmlhead'] .= '<meta property="og:url" content="'.$item["plink"].'" />'."\n";
$page['htmlhead'] .= '<meta property="og:description" content="'.$description.'" />'."\n";
$page['htmlhead'] .= '<meta name="og:article:author" content="'.$author_name.'" />'."\n";
// article:tag
return $o;
}
function displayShowFeed(int $uri_id, int $uid, bool $conversation)
{
$xml = DFRN::itemFeed($uri_id, $uid, $conversation);
if ($xml == '') {
throw new HTTPException\InternalServerErrorException(DI::l10n()->t('The feed for this item is unavailable.'));
}
System::httpExit($xml, Response::TYPE_ATOM);
}

View file

@ -1,36 +0,0 @@
<?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/>.
*
* See update_profile.php for documentation
*/
use Friendica\App;
use Friendica\Core\System;
use Friendica\DI;
require_once "mod/display.php";
function update_display_content(App $a)
{
$profile_uid = intval($_GET["p"]);
$text = display_content($a, true, $profile_uid);
System::htmlUpdateExit($text);
}

View file

@ -493,7 +493,7 @@ class Conversation
if (!$update) { if (!$update) {
$live_update_div = '<div id="live-notes"></div>' . "\r\n" $live_update_div = '<div id="live-notes"></div>' . "\r\n"
. "<script> var profile_uid = " . $this->session->getLocalUserId() . "<script> var profile_uid = " . $this->session->getLocalUserId()
. "; var netargs = '/?f='; </script>\r\n"; . "; var netargs = '?f='; </script>\r\n";
} }
} elseif ($mode === 'display') { } elseif ($mode === 'display') {
$items = $this->addChildren($items, false, $order, $uid, $mode); $items = $this->addChildren($items, false, $order, $uid, $mode);
@ -520,7 +520,7 @@ class Conversation
if (!$update) { if (!$update) {
$live_update_div = '<div id="live-contact"></div>' . "\r\n" $live_update_div = '<div id="live-contact"></div>' . "\r\n"
. "<script> var profile_uid = -1; var netargs = '" . substr($this->args->getCommand(), 8) . "<script> var profile_uid = -1; var netargs = '" . substr($this->args->getCommand(), 8)
."/?f='; </script>\r\n"; ."?f='; </script>\r\n";
} }
} elseif ($mode === 'search') { } elseif ($mode === 'search') {
$live_update_div = '<div id="live-search"></div>' . "\r\n"; $live_update_div = '<div id="live-search"></div>' . "\r\n";

View file

@ -3476,4 +3476,17 @@ class Contact
return []; return [];
} }
/**
* Checks, if contacts with the given condition exists
*
* @param array $condition
*
* @return bool
* @throws \Exception
*/
public static function exists(array $condition): bool
{
return DBA::exists('contact', $condition);
}
} }

361
src/Module/Item/Display.php Normal file
View file

@ -0,0 +1,361 @@
<?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\Item;
use Friendica\App;
use Friendica\BaseModule;
use Friendica\Content\Conversation;
use Friendica\Content\Item as ContentItem;
use Friendica\Content\Text\BBCode;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n;
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues;
use Friendica\Core\Renderer;
use Friendica\Core\Session\Capability\IHandleUserSessions;
use Friendica\Model\Contact;
use Friendica\Model\Item;
use Friendica\Model\Post;
use Friendica\Model\Profile;
use Friendica\Model\User;
use Friendica\Module\Response;
use Friendica\Navigation\Notifications\Repository\Notification;
use Friendica\Navigation\Notifications\Repository\Notify;
use Friendica\Protocol\ActivityPub;
use Friendica\Util\Network;
use Friendica\Util\Profiler;
use Friendica\Network\HTTPException;
use Friendica\Content\Widget;
use Psr\Log\LoggerInterface;
/**
* Controller to display one item and its conversation
*/
class Display extends BaseModule
{
/** @var App\Page */
protected $page;
/** @var IManageConfigValues */
protected $config;
/** @var IManagePersonalConfigValues */
protected $pConfig;
/** @var IHandleUserSessions */
protected $session;
/** @var App */
protected $app;
/** @var ContentItem */
protected $contentItem;
/** @var Conversation */
protected $conversation;
/** @var Notification */
protected $notification;
/** @var Notify */
protected $notify;
public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IManageConfigValues $config, IManagePersonalConfigValues $pConfig, IHandleUserSessions $session, App $app, App\Page $page, ContentItem $contentItem, Conversation $conversation, Notification $notification, Notify $notify, array $server, array $parameters = [])
{
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
$this->page = $page;
$this->config = $config;
$this->pConfig = $pConfig;
$this->session = $session;
$this->app = $app;
$this->contentItem = $contentItem;
$this->conversation = $conversation;
$this->notification = $notification;
$this->notify = $notify;
}
protected function content(array $request = []): string
{
if (ActivityPub::isRequest()) {
$this->baseUrl->redirect(str_replace('display/', 'objects/', $this->args->getQueryString()));
}
if ($this->config->get('system', 'block_public') && !$this->session->isAuthenticated()) {
throw new HTTPException\UnauthorizedException($this->t('Access denied.'));
}
$guid = $this->parameters['guid'] ?? 0;
$item = null;
$itemUid = $this->session->getLocalUserId();
$fields = ['uri-id', 'parent-uri-id', 'author-id', 'author-link', 'body', 'uid', 'guid', 'gravity'];
// Does the local user have this item?
if ($this->session->getLocalUserId()) {
$item = Post::selectFirstForUser($this->session->getLocalUserId(), $fields, [
'guid' => $guid,
'uid' => $this->session->getLocalUserId()
]);
}
// Is this item private but could be visible to the remove visitor?
if (empty($item) && $this->session->getRemoteUserId()) {
$item = Post::selectFirst($fields, ['guid' => $guid, 'private' => Item::PRIVATE, 'origin' => true]);
if (!empty($item)) {
if (!Contact::isFollower($this->session->getRemoteUserId(), $item['uid'])) {
$item = null;
} else {
$itemUid = $item['uid'];
}
}
}
// Is it an item with uid = 0?
if (empty($item)) {
$item = Post::selectFirstForUser($this->session->getLocalUserId(), $fields, [
'guid' => $guid,
'private' => [Item::PUBLIC, Item::UNLISTED],
'uid' => 0
]);
}
if (empty($item)) {
throw new HTTPException\NotFoundException($this->t('The requested item doesn\'t exist or has been deleted.'));
}
if ($item['gravity'] != Item::GRAVITY_PARENT) {
$parent = Post::selectFirstForUser($itemUid, $fields, [
'uid' => [0, $itemUid],
'uri-id' => $item['parent-uri-id']
], ['order' => ['uid' => true]]);
$item = $parent ?: $item;
}
if (!$this->pConfig->get($this->session->getLocalUserId(), 'system', 'detailed_notif')) {
$this->notification->setAllSeenForUser($this->session->getLocalUserId(), ['parent-uri-id' => $item['parent-uri-id']]);
$this->notify->setAllSeenForUser($this->session->getLocalUserId(), ['parent-uri-id' => $item['parent-uri-id']]);
}
$this->displaySidebar($item);
$this->displayHead($item['uri-id'], $item['parent-uri-id']);
$output = '';
// add the uri-id to the update_display parameter
if ($this->session->getLocalUserId()) {
$output .= "<script> var netargs = '?uri_id=" . $item['uri-id'] . "'; </script>";
}
$output .= $this->getDisplayData($item);
return $output;
}
/**
* Loads the content for the sidebar of the display page
*
* @param array $item The current item
*
* @return void
* @throws HTTPException\InternalServerErrorException
* @throws HTTPException\NotFoundException
* @throws \ImagickException
*/
protected function displaySidebar(array $item)
{
$shared = $this->contentItem->getSharedPost($item, ['author-link']);
if (!empty($shared) && empty($shared['comment'])) {
$author = Contact::getByURLForUser($shared['post']['author-link'], $this->session->getLocalUserId());
}
if (empty($contact)) {
$author = Contact::getById($item['author-id']);
}
if (Network::isLocalLink($author['url'])) {
Profile::load($this->app, $author['nick'], false);
} else {
$this->page['aside'] = Widget\VCard::getHTML($author);
}
$this->app->setProfileOwner($item['uid']);
}
protected function getDisplayData(array $item, bool $update = false, int $updateUid = 0, bool $force = false): string
{
$isRemoteContact = false;
$itemUid = $this->session->getLocalUserId();
$parent = null;
if (!$this->session->getLocalUserId() && !empty($item['parent-uri-id'])) {
$parent = Post::selectFirst(['uid'], ['uri-id' => $item['parent-uri-id'], 'wall' => true]);
}
if (!empty($parent)) {
$pageUid = $parent['uid'];
$isRemoteContact = $this->session->getRemoteContactID($pageUid);
if ($isRemoteContact) {
$itemUid = $parent['uid'];
}
} else {
$pageUid = $item['uid'];
}
if (!empty($pageUid) && ($pageUid != $this->session->getLocalUserId())) {
$page_user = User::getById($pageUid, ['hidewall']);
}
$is_owner = $this->session->getLocalUserId() && (in_array($pageUid, [$this->session->getLocalUserId(), 0]));
if (!empty($page_user['hidewall']) && !$is_owner && !$isRemoteContact) {
throw new HTTPException\ForbiddenException($this->t('Access to this profile has been restricted.'));
}
$sql_extra = Item::getPermissionsSQLByUserId($pageUid);
if ($this->session->getLocalUserId() && ($this->session->getLocalUserId() == $pageUid)) {
$unseen = Post::exists([
'parent-uri-id' => $item['parent-uri-id'],
'uid' => $this->session->getLocalUserId(),
'unseen' => true
]);
} else {
$unseen = false;
}
if ($update && !$unseen && !$force) {
return '';
}
$condition = ["`uri-id` = ? AND `uid` IN (0, ?) " . $sql_extra, $item['uri-id'], $itemUid];
$fields = [
'parent-uri-id', 'body', 'title', 'author-name', 'author-avatar', 'plink', 'author-id',
'owner-id', 'contact-id'
];
$item = Post::selectFirstForUser($pageUid, $fields, $condition);
if (empty($item)) {
throw new HTTPException\NotFoundException($this->t('The requested item doesn\'t exist or has been deleted.'));
}
$item['uri-id'] = $item['parent-uri-id'];
if ($unseen) {
$condition = [
'parent-uri-id' => $item['parent-uri-id'],
'uid' => $this->session->getLocalUserId(),
'unseen' => true
];
Item::update(['unseen' => false], $condition);
}
$this->addMetaTags($item);
$output = '';
// We need the editor here to be able to reshare an item.
if ($is_owner && !$update) {
$output .= $this->conversation->statusEditor([], 0, true);
}
$output .= $this->conversation->create([$item], 'display', $updateUid, false, 'commented', $itemUid);
return $output;
}
// We are displaying an "alternate" link if that post was public. See issue 2864
protected function displayHead(string $uriId, string $parentUriId)
{
if (Post::exists(['uri-id' => $uriId, 'private' => [Item::PUBLIC, Item::UNLISTED]])) {
// For the atom feed the nickname doesn't matter at all, we only need the item id.
$this->page['htmlhead'] .= Renderer::replaceMacros(Renderer::getMarkupTemplate('display-head.tpl'), [
'$alternate' => sprintf('display/feed-item/%s.atom', $uriId),
'$conversation' => sprintf('display/feed-item/%s/conversation.atom', $parentUriId)
]);
}
}
/**
* Adds <meta> tags to the HTML output based on an item
*
* @param array $item The item with the information for the <meta> tags
*
* @return void
* @throws \Exception
*/
protected function addMetaTags(array $item)
{
// Preparing the meta header
$description = trim(BBCode::toPlaintext($item['body']));
$title = trim(BBCode::toPlaintext($item['title'] ?? ''));
$author_name = $item['author-name'];
$image = $this->baseUrl->remove($item['author-avatar']);
if ($title === '') {
$title = $author_name;
}
// Limit the description to 160 characters
if (strlen($description) > 160) {
$description = substr($description, 0, 157) . '...';
}
$description = htmlspecialchars($description, ENT_COMPAT, 'UTF-8', true); // allow double encoding here
$title = htmlspecialchars($title, ENT_COMPAT, 'UTF-8', true); // allow double encoding here
$author_name = htmlspecialchars($author_name, ENT_COMPAT, 'UTF-8', true); // allow double encoding here
$page = $this->page;
if (Contact::exists([
'unsearchable' => true, 'id' => [$item['contact-id'], $item['author-id'], $item['owner-id']]
])) {
$page['htmlhead'] .= "<meta content=\"noindex, noarchive\" name=\"robots\" />\n";
}
$page['htmlhead'] .= sprintf("<meta name=\"author\" content=\"%s\" />\n", $author_name);
$page['htmlhead'] .= sprintf("<meta name=\"title\" content=\"%s\" />\n", $title);
$page['htmlhead'] .= sprintf("<meta name=\"fulltitle\" content=\"%s\" />\n", $title);
$page['htmlhead'] .= sprintf("<meta name=\"description\" content=\"%s\" />\n", $description);
// Schema.org microdata
$page['htmlhead'] .= sprintf("<meta itemprop=\"name\" content=\"%s\" />\n", $title);
$page['htmlhead'] .= sprintf("<meta itemprop=\"description\" content=\"%s\" />\n", $description);
$page['htmlhead'] .= sprintf("<meta itemprop=\"image\" content=\"%s\" />\n", $image);
$page['htmlhead'] .= sprintf("<meta itemprop=\"author\" content=\"%s\" />\n", $author_name);
// Twitter cards
$page['htmlhead'] .= "<meta name=\"twitter:card\" content=\"summary\" />\n";
$page['htmlhead'] .= sprintf("<meta name=\"twitter:title\" content=\"%s\" />\n", $title);
$page['htmlhead'] .= sprintf("<meta name=\"twitter:description\" content=\"%s\" />\n", $description);
$page['htmlhead'] .= sprintf("<meta name=\"twitter:image\" content=\"%s/%s\" />\n", $this->baseUrl, $image);
$page['htmlhead'] .= sprintf("<meta name=\"twitter:url\" content=\"%s\" />\n", $item["plink"]);
// Dublin Core
$page['htmlhead'] .= sprintf("<meta name=\"DC.title\" content=\"%s\" />\n", $title);
$page['htmlhead'] .= sprintf("<meta name=\"DC.description\" content=\"%s\" />\n", $description);
// Open Graph
$page['htmlhead'] .= "<meta property=\"og:type\" content=\"website\" />\n";
$page['htmlhead'] .= sprintf("<meta property=\"og:title\" content=\"%s\" />\n", $title);
$page['htmlhead'] .= sprintf("<meta property=\"og:image\" content=\"%s/%s\" />\n", $this->baseUrl, $image);
$page['htmlhead'] .= sprintf("<meta property=\"og:url\" content=\"%s\" />\n", $item["plink"]);
$page['htmlhead'] .= sprintf("<meta property=\"og:description\" content=\"%s\" />\n", $description);
$page['htmlhead'] .= sprintf("<meta name=\"og:article:author\" content=\"%s\" />\n", $author_name);
// article:tag
}
}

91
src/Module/Item/Feed.php Normal file
View file

@ -0,0 +1,91 @@
<?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\Item;
use Friendica\App;
use Friendica\BaseModule;
use Friendica\Core\Config\Capability\IManageConfigValues;
use Friendica\Core\L10n;
use Friendica\Core\Session\Capability\IHandleUserSessions;
use Friendica\Core\System;
use Friendica\Model\Item;
use Friendica\Model\Post;
use Friendica\Module\Response;
use Friendica\Protocol\DFRN;
use Friendica\Util\Profiler;
use Friendica\Network\HTTPException;
use Psr\Log\LoggerInterface;
/**
* Controller to display an item (or the whole conversation of an item) as an ATOM Feed
*/
class Feed extends BaseModule
{
/** @var IManageConfigValues */
protected $config;
/** @var IHandleUserSessions */
protected $session;
public function __construct(L10n $l10n, App\BaseURL $baseUrl, App\Arguments $args, LoggerInterface $logger, Profiler $profiler, Response $response, IManageConfigValues $config, IHandleUserSessions $session, array $server, array $parameters = [])
{
parent::__construct($l10n, $baseUrl, $args, $logger, $profiler, $response, $server, $parameters);
$this->config = $config;
$this->session = $session;
}
protected function rawContent(array $request = [])
{
if ($this->config->get('system', 'block_public') && !$this->session->isAuthenticated()) {
throw new HTTPException\UnauthorizedException($this->t('Access denied.'));
}
$uriId = $this->parameters['uri-id'];
$item = Post::selectFirstForUser($this->session->getLocalUserId(), [
'uri-id',
'parent-uri-id',
'author-id',
'author-link',
'body',
'uid',
'guid',
'gravity',
], [
'uri-id' => $uriId,
'private' => [Item::PUBLIC, Item::UNLISTED],
'uid' => 0,
]);
if (empty($item)) {
throw new HTTPException\BadRequestException($this->t('Item not found.', ['uri-id' => $uriId]));
}
$xml = DFRN::itemFeed($item['uri-id'], $item['uid'], ($this->parameters['mode'] ?? '') === 'conversation');
if (empty($xml)) {
throw new HTTPException\InternalServerErrorException($this->t('The feed for this item is unavailable.', ['uri-id' => $uriId]));
}
System::httpExit($xml, Response::TYPE_ATOM);
}
}

View file

@ -0,0 +1,88 @@
<?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\Update;
use Friendica\Model\Post;
use Friendica\Module\Item\Display as DisplayModule;
use Friendica\Util\DateTimeFormat;
use Friendica\Network\HTTPException;
/**
* Asynchronous update class for the display
*/
class Display extends DisplayModule
{
protected function content(array $request = []): string
{
if ($this->config->get('system', 'block_public') && !$this->session->isAuthenticated()) {
throw new HTTPException\UnauthorizedException($this->t('Access denied.'));
}
$profileUid = $request['p'] ?? 0;
$force = $request['force'] ?? false;
$uriId = $request['uri_id'] ?? 0;
if (empty($uriId)) {
throw new HTTPException\BadRequestException($this->t('Parameter uri_id is missing.'));
}
$item = Post::selectFirst(
['uid', 'parent-uri-id', 'uri-id'],
['uri-id' => $uriId, 'uid' => [0, $profileUid]],
['order' => ['uid' => true]]
);
if (empty($item)) {
throw new HTTPException\NotFoundException($this->t('The requested item doesn\'t exist or has been deleted.'));
}
$this->app->setProfileOwner($item['uid'] ?: $profileUid);
$parentUriId = $item['parent-uri-id'];
if (empty($force)) {
$browserUpdate = $this->pConfig->get($profileUid, 'system', 'update_interval');
if (!empty($browserUpdate)) {
$updateDate = date(DateTimeFormat::MYSQL, time() - (intval($browserUpdate) / 500));
if (!Post::exists([
"`parent-uri-id` = ? AND `uid` IN (?, ?) AND `received` > ?",
$parentUriId, 0,
$profileUid, $updateDate])) {
$this->logger->debug('No updated content. Ending process',
['uri-id' => $uriId, 'uid' => $profileUid, 'updated' => $updateDate]);
return '';
} else {
$this->logger->debug('Updated content found.',
['uri-id' => $uriId, 'uid' => $profileUid, 'updated' => $updateDate]);
}
}
} else {
$this->logger->debug('Forced content update.', ['uri-id' => $uriId, 'uid' => $profileUid]);
}
if (!$this->pConfig->get($this->session->getLocalUserId(), 'system', 'detailed_notif')) {
$this->notification->setAllSeenForUser($this->session->getLocalUserId(), ['parent-uri-id' => $item['parent-uri-id']]);
$this->notify->setAllSeenForUser($this->session->getLocalUserId(), ['parent-uri-id' => $item['parent-uri-id']]);
}
return $this->getDisplayData($item, true, $profileUid, $force);
}
}

View file

@ -398,6 +398,10 @@ return [
'/dirfind' => [Module\Search\Directory::class, [R::GET]], '/dirfind' => [Module\Search\Directory::class, [R::GET]],
'/directory' => [Module\Directory::class, [R::GET]], '/directory' => [Module\Directory::class, [R::GET]],
'/display/{guid}' => [Module\Item\Display::class, [R::GET]],
'/display/feed-item/{uri-id}[.atom]' => [Module\Item\Feed::class, [R::GET]],
'/display/feed-item/{uri-id}/{mode:conversation}[.atom]' => [Module\Item\Feed::class, [R::GET]],
'/featured/{nickname}' => [Module\ActivityPub\Featured::class, [R::GET]], '/featured/{nickname}' => [Module\ActivityPub\Featured::class, [R::GET]],
'/feed' => [ '/feed' => [
@ -638,6 +642,8 @@ return [
'/update_community[/{content}]' => [Module\Update\Community::class, [R::GET]], '/update_community[/{content}]' => [Module\Update\Community::class, [R::GET]],
'/update_display' => [Module\Update\Display::class, [R::GET]],
'/update_network' => [ '/update_network' => [
'[/]' => [Module\Update\Network::class, [R::GET]], '[/]' => [Module\Update\Network::class, [R::GET]],
'/archive/{from:\d\d\d\d-\d\d-\d\d}[/{to:\d\d\d\d-\d\d-\d\d}]' => [Module\Update\Network::class, [R::GET]], '/archive/{from:\d\d\d\d-\d\d-\d\d}[/{to:\d\d\d\d-\d\d-\d\d}]' => [Module\Update\Network::class, [R::GET]],

View file

@ -590,7 +590,7 @@ function liveUpdate(src) {
var orgHeight = $("section").height(); var orgHeight = $("section").height();
var udargs = ((netargs.length) ? '/' + netargs : ''); var udargs = ((netargs.length) ? netargs : '');
var update_url = 'update_' + src + udargs + '&p=' + profile_uid + '&force=' + (force ? 1 : 0) + '&item=' + update_item; var update_url = 'update_' + src + udargs + '&p=' + profile_uid + '&force=' + (force ? 1 : 0) + '&item=' + update_item;

View file

@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: 2022.12-dev\n" "Project-Id-Version: 2022.12-dev\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2022-11-14 17:05-0500\n" "POT-Creation-Date: 2022-11-15 00:00+0000\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
@ -18,26 +18,6 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: mod/display.php:140 mod/photos.php:797
#: src/Module/Conversation/Community.php:187 src/Module/Directory.php:48
#: src/Module/Search/Index.php:64
msgid "Public access denied."
msgstr ""
#: mod/display.php:210 mod/display.php:284
msgid "The requested item doesn't exist or has been deleted."
msgstr ""
#: mod/display.php:259 src/Model/Event.php:525
#: src/Module/Profile/Profile.php:93 src/Module/Profile/Profile.php:108
#: src/Module/Profile/Status.php:109 src/Module/Update/Profile.php:55
msgid "Access to this profile has been restricted."
msgstr ""
#: mod/display.php:364
msgid "The feed for this item is unavailable."
msgstr ""
#: mod/editpost.php:38 mod/item.php:181 mod/item.php:186 mod/item.php:870 #: mod/editpost.php:38 mod/item.php:181 mod/item.php:186 mod/item.php:870
#: mod/message.php:69 mod/message.php:114 mod/notes.php:44 mod/photos.php:159 #: mod/message.php:69 mod/message.php:114 mod/notes.php:44 mod/photos.php:159
#: mod/photos.php:886 src/Module/Attach.php:56 src/Module/BaseApi.php:94 #: mod/photos.php:886 src/Module/Attach.php:56 src/Module/BaseApi.php:94
@ -294,7 +274,7 @@ msgstr ""
#: mod/item.php:846 src/Module/Admin/Themes/Details.php:39 #: mod/item.php:846 src/Module/Admin/Themes/Details.php:39
#: src/Module/Admin/Themes/Index.php:59 src/Module/Debug/ItemBody.php:42 #: src/Module/Admin/Themes/Index.php:59 src/Module/Debug/ItemBody.php:42
#: src/Module/Debug/ItemBody.php:57 #: src/Module/Debug/ItemBody.php:57 src/Module/Item/Feed.php:81
msgid "Item not found." msgid "Item not found."
msgstr "" msgstr ""
@ -670,6 +650,11 @@ msgstr ""
msgid "Image upload failed." msgid "Image upload failed."
msgstr "" msgstr ""
#: mod/photos.php:797 src/Module/Conversation/Community.php:187
#: src/Module/Directory.php:48 src/Module/Search/Index.php:64
msgid "Public access denied."
msgstr ""
#: mod/photos.php:802 #: mod/photos.php:802
msgid "No photos selected" msgid "No photos selected"
msgstr "" msgstr ""
@ -3052,6 +3037,12 @@ msgstr ""
msgid "No events to display" msgid "No events to display"
msgstr "" msgstr ""
#: src/Model/Event.php:525 src/Module/Item/Display.php:221
#: src/Module/Profile/Profile.php:93 src/Module/Profile/Profile.php:108
#: src/Module/Profile/Status.php:109 src/Module/Update/Profile.php:55
msgid "Access to this profile has been restricted."
msgstr ""
#: src/Model/Event.php:567 src/Module/Calendar/Event/Show.php:60 #: src/Model/Event.php:567 src/Module/Calendar/Event/Show.php:60
msgid "Event not found." msgid "Event not found."
msgstr "" msgstr ""
@ -5863,9 +5854,10 @@ msgstr[1] ""
#: src/Module/Contact/Follow.php:69 src/Module/Contact/Redir.php:62 #: src/Module/Contact/Follow.php:69 src/Module/Contact/Redir.php:62
#: src/Module/Contact/Redir.php:222 src/Module/Conversation/Community.php:193 #: src/Module/Contact/Redir.php:222 src/Module/Conversation/Community.php:193
#: src/Module/Debug/ItemBody.php:38 src/Module/Diaspora/Receive.php:57 #: src/Module/Debug/ItemBody.php:38 src/Module/Diaspora/Receive.php:57
#: src/Module/Item/Display.php:93 src/Module/Item/Feed.php:60
#: src/Module/Item/Follow.php:41 src/Module/Item/Ignore.php:41 #: src/Module/Item/Follow.php:41 src/Module/Item/Ignore.php:41
#: src/Module/Item/Pin.php:41 src/Module/Item/Pin.php:56 #: src/Module/Item/Pin.php:41 src/Module/Item/Pin.php:56
#: src/Module/Item/Star.php:42 #: src/Module/Item/Star.php:42 src/Module/Update/Display.php:38
msgid "Access denied." msgid "Access denied."
msgstr "" msgstr ""
@ -7124,6 +7116,15 @@ msgid ""
"<a href=\"/settings/display\">Theme Customization settings</a>." "<a href=\"/settings/display\">Theme Customization settings</a>."
msgstr "" msgstr ""
#: src/Module/Item/Display.php:133 src/Module/Item/Display.php:247
#: src/Module/Update/Display.php:56
msgid "The requested item doesn't exist or has been deleted."
msgstr ""
#: src/Module/Item/Feed.php:87
msgid "The feed for this item is unavailable."
msgstr ""
#: src/Module/Item/Follow.php:51 #: src/Module/Item/Follow.php:51
msgid "Unable to follow this item." msgid "Unable to follow this item."
msgstr "" msgstr ""
@ -10208,6 +10209,10 @@ msgstr ""
msgid "Privacy Statement" msgid "Privacy Statement"
msgstr "" msgstr ""
#: src/Module/Update/Display.php:46
msgid "Parameter uri_id is missing."
msgstr ""
#: src/Module/User/Import.php:103 #: src/Module/User/Import.php:103
msgid "User imports on closed servers can only be done by an administrator." msgid "User imports on closed servers can only be done by an administrator."
msgstr "" msgstr ""