<?php
/**
- * @copyright Copyright (C) 2010-2022, the Friendica project
+ * @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
use DOMDocument;
use DOMElement;
use DOMXPath;
+use Friendica\App;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
use Friendica\Core\Cache\Enum\Duration;
*/
if ($aliaslink != '') {
$contact = DBA::selectFirst('contact', [], [
- "`uid` = ? AND `alias` = ? AND `network` != ? AND `rel` IN (?, ?)",
+ "`uid` = ? AND `alias` = ? AND `rel` IN (?, ?)",
$importer['uid'],
- $aliaslink, Protocol::STATUSNET,
+ $aliaslink,
Contact::SHARING, Contact::FRIEND,
]);
}
}
$contact = DBA::selectFirst('contact', [], [
- "`uid` = ? AND `nurl` IN (?, ?) AND `network` != ? AND `rel` IN (?, ?)",
+ "`uid` = ? AND `nurl` IN (?, ?) AND `rel` IN (?, ?)",
$importer['uid'],
Strings::normaliseLink($author['author-link']),
Strings::normaliseLink($aliaslink),
- Protocol::STATUSNET,
Contact::SHARING,
Contact::FRIEND,
]);
if (!DBA::isResult($contact) && ($addr != '')) {
$contact = DBA::selectFirst('contact', [], [
- "`uid` = ? AND `addr` = ? AND `network` != ? AND `rel` IN (?, ?)",
+ "`uid` = ? AND `addr` = ? AND `rel` IN (?, ?)",
$importer['uid'],
$addr,
- Protocol::STATUSNET,
Contact::SHARING,
Contact::FRIEND,
]);
}
}
- $header = [];
- $header['uid'] = $importer['uid'];
- $header['network'] = Protocol::OSTATUS;
- $header['wall'] = 0;
- $header['origin'] = 0;
- $header['gravity'] = GRAVITY_COMMENT;
+ // Initial header elements
+ $header = [
+ 'uid' => $importer['uid'],
+ 'network' => Protocol::OSTATUS,
+ 'wall' => 0,
+ 'origin' => 0,
+ 'gravity' => Item::GRAVITY_COMMENT,
+ ];
if (!is_object($doc->firstChild) || empty($doc->firstChild->tagName)) {
return false;
$orig_uri = $xpath->query('activity:object/atom:id', $entry)->item(0)->nodeValue;
Logger::notice('Favorite', ['uri' => $orig_uri, 'item' => $item]);
- $item['verb'] = Activity::LIKE;
+ $item['body'] = $item['verb'] = Activity::LIKE;
$item['thr-parent'] = $orig_uri;
- $item['gravity'] = GRAVITY_ACTIVITY;
+ $item['gravity'] = Item::GRAVITY_ACTIVITY;
$item['object-type'] = Activity\ObjectType::NOTE;
}
$item['created'] = XML::getFirstNodeValue($xpath, 'atom:published/text()', $entry);
$item['edited'] = XML::getFirstNodeValue($xpath, 'atom:updated/text()', $entry);
- $item['conversation-uri'] = XML::getFirstNodeValue($xpath, 'ostatus:conversation/text()', $entry);
+ $item['conversation'] = XML::getFirstNodeValue($xpath, 'ostatus:conversation/text()', $entry);
$conv = $xpath->query('ostatus:conversation', $entry);
if (is_object($conv->item(0))) {
foreach ($conv->item(0)->attributes as $attributes) {
if ($attributes->name == 'ref') {
- $item['conversation-uri'] = $attributes->textContent;
+ $item['conversation'] = $attributes->textContent;
}
if ($attributes->name == 'href') {
- $item['conversation-href'] = $attributes->textContent;
+ $item['conversation'] = $attributes->textContent;
}
}
}
}
}
- if (($self != '') && empty($item['protocol'])) {
- self::fetchSelf($self, $item);
- }
-
- if (!empty($item['conversation-href'])) {
- self::fetchConversation($item['conversation-href'], $item['conversation-uri']);
- }
-
if (isset($item['thr-parent'])) {
if (!Post::exists(['uid' => $importer['uid'], 'uri' => $item['thr-parent']])) {
if ($related != '') {
}
} else {
$item['thr-parent'] = $item['uri'];
- $item['gravity'] = GRAVITY_PARENT;
- }
-
- if (($item['author-link'] != '') && !empty($item['protocol'])) {
- $item = Conversation::insert($item);
+ $item['gravity'] = Item::GRAVITY_PARENT;
}
self::$itemlist[] = $item;
}
- /**
- * Fetch the conversation for posts
- *
- * @param string $conversation The link to the conversation
- * @param string $conversation_uri The conversation in "uri" format
- * @return void
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
- */
- private static function fetchConversation(string $conversation, string $conversation_uri)
- {
- // Ensure that we only store a conversation once in a process
- if (isset(self::$conv_list[$conversation])) {
- return;
- }
-
- self::$conv_list[$conversation] = true;
-
- $curlResult = DI::httpClient()->get($conversation, HttpClientAccept::ATOM_XML);
-
- if (!$curlResult->isSuccess() || empty($curlResult->getBody())) {
- return;
- }
-
- $xml = '';
-
- if ($curlResult->inHeader('Content-Type') &&
- in_array('application/atom+xml', $curlResult->getHeader('Content-Type'))) {
- $xml = $curlResult->getBody();
- }
-
- if ($xml == '') {
- $doc = new DOMDocument();
- if (!@$doc->loadHTML($curlResult->getBody())) {
- return;
- }
- $xpath = new DOMXPath($doc);
-
- $links = $xpath->query('//link');
- if ($links) {
- $file = '';
- foreach ($links as $link) {
- $attribute = self::readAttributes($link);
- if (($attribute['rel'] == 'alternate') && ($attribute['type'] == 'application/atom+xml')) {
- $file = $attribute['href'];
- }
- }
- if ($file != '') {
- $conversation_atom = DI::httpClient()->get($attribute['href'], HttpClientAccept::ATOM_XML);
-
- if ($conversation_atom->isSuccess()) {
- $xml = $conversation_atom->getBody();
- }
- }
- }
- }
-
- if ($xml == '') {
- return;
- }
-
- self::storeConversation($xml, $conversation, $conversation_uri);
- }
-
- /**
- * Store a feed in several conversation entries
- *
- * @param string $xml The feed
- * @param string $conversation conversation
- * @param string $conversation_uri conversation uri
- * @return void
- * @throws \Exception
- */
- private static function storeConversation(string $xml, string $conversation = '', string $conversation_uri = '')
- {
- $doc = new DOMDocument();
- @$doc->loadXML($xml);
-
- $xpath = new DOMXPath($doc);
- $xpath->registerNamespace('atom', ActivityNamespace::ATOM1);
- $xpath->registerNamespace('thr', ActivityNamespace::THREAD);
- $xpath->registerNamespace('ostatus', ActivityNamespace::OSTATUS);
-
- $entries = $xpath->query('/atom:feed/atom:entry');
-
- // Now store the entries
- foreach ($entries as $entry) {
- $doc2 = new DOMDocument();
- $doc2->preserveWhiteSpace = false;
- $doc2->formatOutput = true;
-
- $conv_data = [];
-
- $conv_data['protocol'] = Conversation::PARCEL_SPLIT_CONVERSATION;
- $conv_data['direction'] = Conversation::PULL;
- $conv_data['network'] = Protocol::OSTATUS;
- $conv_data['uri'] = XML::getFirstNodeValue($xpath, 'atom:id/text()', $entry);
-
- $inreplyto = $xpath->query('thr:in-reply-to', $entry);
- if (is_object($inreplyto->item(0))) {
- foreach ($inreplyto->item(0)->attributes as $attributes) {
- if ($attributes->name == 'ref') {
- $conv_data['reply-to-uri'] = $attributes->textContent;
- }
- }
- }
-
- $conv_data['conversation-uri'] = XML::getFirstNodeValue($xpath, 'ostatus:conversation/text()', $entry);
-
- $conv = $xpath->query('ostatus:conversation', $entry);
- if (is_object($conv->item(0))) {
- foreach ($conv->item(0)->attributes as $attributes) {
- if ($attributes->name == 'ref') {
- $conv_data['conversation-uri'] = $attributes->textContent;
- }
- if ($attributes->name == 'href') {
- $conv_data['conversation-href'] = $attributes->textContent;
- }
- }
- }
-
- if ($conversation != '') {
- $conv_data['conversation-uri'] = $conversation;
- }
-
- if ($conversation_uri != '') {
- $conv_data['conversation-uri'] = $conversation_uri;
- }
-
- $entry = $doc2->importNode($entry, true);
-
- $doc2->appendChild($entry);
-
- $conv_data['source'] = $doc2->saveXML();
-
- Logger::info('Store conversation data for uri '.$conv_data['uri']);
- Conversation::insert($conv_data);
- }
- }
-
- /**
- * Fetch the own post so that it can be stored later
- *
- * We want to store the original data for later processing.
- * This function is meant for cases where we process a feed with multiple entries.
- * In that case we need to fetch the single posts here.
- *
- * @param string $self The link to the self item
- * @param array $item The item array
- * @return void
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
- */
- private static function fetchSelf(string $self, array &$item)
- {
- $condition = ['item-uri' => $self, 'protocol' => [Conversation::PARCEL_DFRN,
- Conversation::PARCEL_DIASPORA_DFRN, Conversation::PARCEL_LOCAL_DFRN,
- Conversation::PARCEL_DIRECT, Conversation::PARCEL_SALMON]];
- if (DBA::exists('conversation', $condition)) {
- Logger::info('Conversation '.$item['uri'].' is already stored.');
- return;
- }
-
- $curlResult = DI::httpClient()->get($self, HttpClientAccept::ATOM_XML);
-
- if (!$curlResult->isSuccess()) {
- return;
- }
-
- // We reformat the XML to make it better readable
- $doc = new DOMDocument();
- $doc->loadXML($curlResult->getBody());
- $doc->preserveWhiteSpace = false;
- $doc->formatOutput = true;
- $xml = $doc->saveXML();
-
- $item['protocol'] = Conversation::PARCEL_SALMON;
- $item['source'] = $xml;
- $item['direction'] = Conversation::PULL;
-
- Logger::info('Conversation '.$item['uri'].' is now fetched.');
- }
-
/**
* Fetch related posts and processes them
*
*/
private static function fetchRelated(string $related, string $related_uri, array $importer)
{
- $condition = [
- 'item-uri' => $related_uri,
- 'protocol' => [
- Conversation::PARCEL_DFRN,
- Conversation::PARCEL_DIASPORA_DFRN,
- Conversation::PARCEL_LOCAL_DFRN,
- Conversation::PARCEL_DIRECT,
- Conversation::PARCEL_SALMON,
- ],
- ];
- $conversation = DBA::selectFirst('conversation', ['source', 'protocol'], $condition);
- if (DBA::isResult($conversation)) {
- $stored = true;
- $xml = $conversation['source'];
- if (self::process($xml, $importer, $contact, $hub, $stored, false, Conversation::PULL)) {
- Logger::info('Got valid cached XML for URI '.$related_uri);
- return;
- }
- if ($conversation['protocol'] == Conversation::PARCEL_SALMON) {
- Logger::info('Delete invalid cached XML for URI '.$related_uri);
- DBA::delete('conversation', ['item-uri' => $related_uri]);
- }
- }
-
$stored = false;
$curlResult = DI::httpClient()->get($related, HttpClientAccept::ATOM_XML);
}
}
- // Finally we take the data that we fetched from "ostatus:conversation"
- if ($xml == '') {
- $condition = ['item-uri' => $related_uri, 'protocol' => Conversation::PARCEL_SPLIT_CONVERSATION];
- $conversation = DBA::selectFirst('conversation', ['source'], $condition);
- if (DBA::isResult($conversation)) {
- $stored = true;
- Logger::info('Got cached XML from conversation for URI ' . $related_uri);
- $xml = $conversation['source'];
- }
- }
-
if ($xml != '') {
+ $hub = '';
self::process($xml, $importer, $contact, $hub, $stored, false, Conversation::PULL);
} else {
Logger::info('XML could not be fetched for URI: ' . $related_uri . ' - href: ' . $related);
case 'ostatus:conversation':
$link_data['conversation'] = $attribute['href'];
- $item['conversation-href'] = $link_data['conversation'];
- if (!isset($item['conversation-uri'])) {
- $item['conversation-uri'] = $item['conversation-href'];
- }
+ $item['conversation'] = $link_data['conversation'];
break;
case 'enclosure':
break;
default:
- Logger::warning('Unsupported rel=' . $attribute['rel'] . ', href=' . $attribute['href'] . ', object-type=' . $attribute['object-type']);
+ Logger::notice('Unsupported rel=' . $attribute['rel'] . ', href=' . $attribute['href'] . ', object-type=' . $item['object-type']);
}
}
}
}
}
- /**
- * Cleans the body of a post if it contains picture links
- *
- * @param string $body The body
- * @param integer $uriId
- * @return string The cleaned body
- * @throws \Friendica\Network\HTTPException\InternalServerErrorException
- */
- public static function formatPicturePost(string $body, int $uriid): string
- {
- $siteinfo = BBCode::getAttachedData($body);
-
- if (($siteinfo['type'] == 'photo') && (!empty($siteinfo['preview']) || !empty($siteinfo['image']))) {
- if (isset($siteinfo['preview'])) {
- $preview = $siteinfo['preview'];
- } else {
- $preview = $siteinfo['image'];
- }
-
- // Is it a remote picture? Then make a smaller preview here
- $preview = Post\Link::getByLink($uriid, $preview, Proxy::SIZE_SMALL);
-
- // Is it a local picture? Then make it smaller here
- $preview = str_replace(['-0.jpg', '-0.png'], ['-2.jpg', '-2.png'], $preview);
- $preview = str_replace(['-1.jpg', '-1.png'], ['-2.jpg', '-2.png'], $preview);
-
- if (isset($siteinfo['url'])) {
- $url = $siteinfo['url'];
- } else {
- $url = $siteinfo['image'];
- }
-
- $body = trim($siteinfo['text']) . ' [url]' . $url . "[/url]\n[img]" . $preview . '[/img]';
- }
-
- return $body;
- }
-
/**
* Adds the header elements to the XML document
*
$attributes = [
'uri' => 'https://friendi.ca',
- 'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION,
+ 'version' => App::VERSION . '-' . DB_UPDATE_VERSION,
];
- XML::addElement($doc, $root, 'generator', FRIENDICA_PLATFORM, $attributes);
+ XML::addElement($doc, $root, 'generator', App::PLATFORM, $attributes);
XML::addElement($doc, $root, 'id', DI::baseUrl() . '/profile/' . $owner['nick']);
XML::addElement($doc, $root, 'title', $title);
XML::addElement($doc, $root, 'subtitle', sprintf("Updates from %s on %s", $owner['name'], DI::config()->get('config', 'sitename')));
*/
public static function getAttachment(DOMDocument $doc, DOMElement $root, array $item)
{
- $siteinfo = BBCode::getAttachedData($item['body']);
-
- switch ($siteinfo['type']) {
- case 'photo':
- if (!empty($siteinfo['image'])) {
- $imgdata = Images::getInfoFromURLCached($siteinfo['image']);
- if ($imgdata) {
- $attributes = [
- 'rel' => 'enclosure',
- 'href' => $siteinfo['image'],
- 'type' => $imgdata['mime'],
- 'length' => intval($imgdata['size']),
- ];
- XML::addElement($doc, $root, 'link', '', $attributes);
- }
- }
- break;
-
- case 'video':
- $attributes = [
- 'rel' => 'enclosure',
- 'href' => $siteinfo['url'],
- 'type' => 'text/html; charset=UTF-8',
- 'length' => '0',
- 'title' => ($siteinfo['title'] ?? '') ?: $siteinfo['url'],
- ];
- XML::addElement($doc, $root, 'link', '', $attributes);
- break;
-
- default:
- Logger::warning('Unsupported type', ['type' => $siteinfo['type'], 'url' => $siteinfo['url'] ?? '']);
- break;
- }
-
- if (!DI::config()->get('system', 'ostatus_not_attach_preview') && ($siteinfo['type'] != 'photo') && isset($siteinfo['image'])) {
- $imgdata = Images::getInfoFromURLCached($siteinfo['image']);
- if ($imgdata) {
- $attributes = [
- 'rel' => 'enclosure',
- 'href' => $siteinfo['image'],
- 'type' => $imgdata['mime'],
- 'length' => intval($imgdata['size']),
- ];
-
- XML::addElement($doc, $root, 'link', '', $attributes);
- }
- }
-
- foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::DOCUMENT, Post\Media::TORRENT, Post\Media::UNKNOWN]) as $attachment) {
+ foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO, Post\Media::DOCUMENT, Post\Media::TORRENT]) as $attachment) {
$attributes = ['rel' => 'enclosure',
'href' => $attachment['url'],
'type' => $attachment['mimetype']];
*/
private static function likeEntry(DOMDocument $doc, array $item, array $owner, bool $toplevel): DOMElement
{
- if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
+ if (($item['gravity'] != Item::GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
Logger::info('OStatus entry is from author ' . $owner['url'] . ' - not from ' . $item['author-link'] . '. Quitting.');
}
}
$item['uri'] = $item['parent-uri'] = $item['thr-parent']
- = 'tag:' . DI::baseUrl()->getHostname().
- ','.date('Y-m-d').':'.$action.':'.$owner['uid'].
+ = 'tag:' . DI::baseUrl()->getHost() .
+ ','.date('Y-m-d').':'.$action.':'.$owner['uid'].
':person:'.$connect_id.':'.$item['created'];
$item['body'] = sprintf($message, $owner['nick'], $contact['nick']);
*/
private static function noteEntry(DOMDocument $doc, array $item, array $owner, bool $toplevel): DOMElement
{
- if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
+ if (($item['gravity'] != Item::GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
Logger::info('OStatus entry is from author ' . $owner['url'] . ' - not from ' . $item['author-link'] . '. Quitting.');
}
if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) {
$contact = Contact::getByURL($item['author-link']) ?: $owner;
- $contact['nickname'] = $contact['nickname'] ?? $contact['nick'];
+ $contact['nickname'] = $contact['nickname'] ?? $contact['nick'];
$author = self::addAuthor($doc, $contact, false);
$entry->appendChild($author);
}
XML::addElement($doc, $entry, 'id', $item['uri']);
XML::addElement($doc, $entry, 'title', html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
- $body = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']);
- $body = self::formatPicturePost($body, $item['uri-id']);
+ $body = Post\Media::addAttachmentsToBody($item['uri-id'], DI::contentItem()->addSharedPost($item));
+ $body = Post\Media::addHTMLLinkToBody($item['uri-id'], $body);
if (!empty($item['title'])) {
$body = '[b]' . $item['title'] . "[/b]\n\n" . $body;
{
$mentioned = [];
- if ($item['gravity'] != GRAVITY_PARENT) {
+ if ($item['gravity'] != Item::GRAVITY_PARENT) {
$parent = Post::selectFirst(['guid', 'author-link', 'owner-link'], ['id' => $item['parent']]);
$thrparent = Post::selectFirst(['guid', 'author-link', 'owner-link', 'plink'], ['uid' => $owner['uid'], 'uri' => $item['thr-parent']]);
}
if (intval($item['parent']) > 0) {
- $conversation_href = $conversation_uri = str_replace('/objects/', '/context/', $item['thr-parent']);
-
- if (isset($parent_item)) {
- $conversation = DBA::selectFirst('conversation', ['conversation-uri', 'conversation-href'], ['item-uri' => $parent_item]);
- if (DBA::isResult($conversation)) {
- if ($conversation['conversation-uri'] != '') {
- $conversation_uri = $conversation['conversation-uri'];
- }
- if ($conversation['conversation-href'] != '') {
- $conversation_href = $conversation['conversation-href'];
- }
- }
- }
+ $conversation_href = $conversation_uri = $item['conversation'];
XML::addElement($doc, $entry, 'link', '', ['rel' => 'ostatus:conversation', 'href' => $conversation_href]);
$previous_created = $last_update;
- // Don't cache when the last item was posted less then 15 minutes ago (Cache duration)
+ // Don't cache when the last item was posted less than 15 minutes ago (Cache duration)
if ((time() - strtotime($owner['last-item'])) < 15*60) {
$result = DI::cache()->get($cachekey);
if (!$nocache && !is_null($result)) {