namespace Friendica\Protocol\ActivityPub;
+use Friendica\Content\PageInfo;
use Friendica\Content\Text\BBCode;
use Friendica\Content\Text\HTML;
use Friendica\Core\Logger;
foreach ($activity['attachments'] as $attach) {
switch ($attach['type']) {
case 'link':
- // Only one [attachment] tag is allowed
- $existingAttachmentPos = strpos($item['body'], '[attachment');
- if ($existingAttachmentPos !== false) {
- $linkTitle = $attach['title'] ?: $attach['url'];
- // Additional link attachments are prepended before the existing [attachment] tag
- $item['body'] = substr_replace($item['body'], "\n[bookmark=" . $attach['url'] . ']' . $linkTitle . "[/bookmark]\n", $existingAttachmentPos, 0);
- } else {
- // Strip the link preview URL from the end of the body if any
- $quotedUrl = preg_quote($attach['url'], '#');
- $item['body'] = preg_replace("#\s*(?:\[bookmark={$quotedUrl}].+?\[/bookmark]|\[url={$quotedUrl}].+?\[/url]|\[url]{$quotedUrl}\[/url]|{$quotedUrl})\s*$#", '', $item['body']);
- $item['body'] .= "\n[attachment type='link' url='" . $attach['url'] . "' title='" . htmlspecialchars($attach['title'] ?? '', ENT_QUOTES) . "' image='" . ($attach['image'] ?? '') . "']" . ($attach['desc'] ?? '') . '[/attachment]';
- }
+ $data = [
+ 'url' => $attach['url'],
+ 'type' => $attach['type'],
+ 'title' => $attach['title'] ?? '',
+ 'text' => $attach['desc'] ?? '',
+ 'image' => $attach['image'] ?? '',
+ 'images' => [],
+ 'keywords' => [],
+ ];
+ $item['body'] = PageInfo::appendDataToBody($item['body'], $data);
break;
default:
$filetype = strtolower(substr($attach['mediaType'], 0, strpos($attach['mediaType'], '/')));
continue 2;
}
+ $item['body'] .= "\n";
+
+ // image is the preview/thumbnail URL
+ if (!empty($attach['image'])) {
+ $item['body'] .= '[url=' . $attach['url'] . ']';
+ $attach['url'] = $attach['image'];
+ }
+
if (empty($attach['name'])) {
- $item['body'] .= "\n[img]" . $attach['url'] . '[/img]';
+ $item['body'] .= '[img]' . $attach['url'] . '[/img]';
} else {
- $item['body'] .= "\n[img=" . $attach['url'] . ']' . $attach['name'] . '[/img]';
+ $item['body'] .= '[img=' . $attach['url'] . ']' . $attach['name'] . '[/img]';
+ }
+
+ if (!empty($attach['image'])) {
+ $item['body'] .= '[/url]';
}
} elseif ($filetype == 'audio') {
if (!empty($activity['source']) && strpos($activity['source'], $attach['url'])) {
$item = Item::selectFirst(['uri', 'uri-id', 'thr-parent', 'gravity'], ['uri' => $activity['id']]);
if (!DBA::isResult($item)) {
Logger::warning('No existing item, item will be created', ['uri' => $activity['id']]);
- self::createItem($activity);
+ $item = self::createItem($activity);
+ self::postItem($activity, $item);
return;
}
* Prepares data for a message
*
* @param array $activity Activity array
+ * @return array Internal item
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
} else {
$item['gravity'] = GRAVITY_COMMENT;
$item['object-type'] = Activity\ObjectType::COMMENT;
-
- // Ensure that the comment reaches all receivers of the referring post
- $activity['receiver'] = self::addReceivers($activity);
}
if (empty($activity['directmessage']) && ($activity['id'] != $activity['reply-to-id']) && !Item::exists(['uri' => $activity['reply-to-id']])) {
$item['diaspora_signed_text'] = $activity['diaspora:comment'] ?? '';
- self::postItem($activity, $item);
+ /// @todo What to do with $activity['context']?
+ if (empty($activity['directmessage']) && ($item['gravity'] != GRAVITY_PARENT) && !Item::exists(['uri' => $item['thr-parent']])) {
+ Logger::info('Parent not found, message will be discarded.', ['thr-parent' => $item['thr-parent']]);
+ return [];
+ }
+
+ $item['network'] = Protocol::ACTIVITYPUB;
+ $item['author-link'] = $activity['author'];
+ $item['author-id'] = Contact::getIdForURL($activity['author']);
+ $item['owner-link'] = $activity['actor'];
+ $item['owner-id'] = Contact::getIdForURL($activity['actor']);
+
+ if (in_array(0, $activity['receiver']) && !empty($activity['unlisted'])) {
+ $item['private'] = Item::UNLISTED;
+ } elseif (in_array(0, $activity['receiver'])) {
+ $item['private'] = Item::PUBLIC;
+ } else {
+ $item['private'] = Item::PRIVATE;
+ }
+
+ if (!empty($activity['raw'])) {
+ $item['source'] = $activity['raw'];
+ $item['protocol'] = Conversation::PARCEL_ACTIVITYPUB;
+ $item['conversation-href'] = $activity['context'] ?? '';
+ $item['conversation-uri'] = $activity['conversation'] ?? '';
+
+ if (isset($activity['push'])) {
+ $item['direction'] = $activity['push'] ? Conversation::PUSH : Conversation::PULL;
+ }
+ }
+
+ $item['isForum'] = false;
+
+ if (!empty($activity['thread-completion'])) {
+ // Store the original actor in the "causer" fields to enable the check for ignored or blocked contacts
+ $item['causer-link'] = $item['owner-link'];
+ $item['causer-id'] = $item['owner-id'];
+
+ Logger::info('Ignoring actor because of thread completion.', ['actor' => $item['owner-link']]);
+ $item['owner-link'] = $item['author-link'];
+ $item['owner-id'] = $item['author-id'];
+ } else {
+ $actor = APContact::getByURL($item['owner-link'], false);
+ $item['isForum'] = ($actor['type'] == 'Group');
+ }
+
+ $item['uri'] = $activity['id'];
+
+ $item['created'] = DateTimeFormat::utc($activity['published']);
+ $item['edited'] = DateTimeFormat::utc($activity['updated']);
+ $guid = $activity['sc:identifier'] ?: self::getGUIDByURL($item['uri']);
+ $item['guid'] = $activity['diaspora:guid'] ?: $guid;
+
+ $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
+
+ $item = self::processContent($activity, $item);
+ if (empty($item)) {
+ Logger::info('Message was not processed');
+ return [];
+ }
+
+ $item['plink'] = $activity['alternate-url'] ?? $item['uri'];
+
+ $item = self::constructAttachList($activity, $item);
+
+ return $item;
}
/**
}
}
- /**
- * Add users to the receiver list of the given public activity.
- * This is used to ensure that the activity will be stored in every thread.
- *
- * @param array $activity Activity array
- * @return array Modified receiver list
- */
- private static function addReceivers(array $activity)
- {
- if (!in_array(0, $activity['receiver'])) {
- // Private activities will not be modified
- return $activity['receiver'];
- }
-
- // Add all owners of the referring item to the receivers
- $original = $receivers = $activity['receiver'];
- $items = Item::select(['uid'], ['uri' => $activity['object_id']]);
- while ($item = DBA::fetch($items)) {
- $receivers['uid:' . $item['uid']] = $item['uid'];
- }
- DBA::close($items);
-
- if (count($original) != count($receivers)) {
- Logger::info('Improved data', ['id' => $activity['id'], 'object' => $activity['object_id'], 'original' => $original, 'improved' => $receivers]);
- }
-
- return $receivers;
- }
-
/**
* Prepare the item array for an activity
*
*/
public static function createActivity($activity, $verb)
{
- $item = [];
+ $item = self::createItem($activity);
$item['verb'] = $verb;
$item['thr-parent'] = $activity['object_id'];
$item['gravity'] = GRAVITY_ACTIVITY;
$item['diaspora_signed_text'] = $activity['diaspora:like'] ?? '';
- $activity['receiver'] = self::addReceivers($activity);
-
self::postItem($activity, $item);
}
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- private static function postItem($activity, $item)
+ public static function postItem(array $activity, array $item)
{
- /// @todo What to do with $activity['context']?
- if (empty($activity['directmessage']) && ($item['gravity'] != GRAVITY_PARENT) && !Item::exists(['uri' => $item['thr-parent']])) {
- Logger::info('Parent not found, message will be discarded.', ['thr-parent' => $item['thr-parent']]);
- return;
- }
-
- $item['network'] = Protocol::ACTIVITYPUB;
- $item['author-link'] = $activity['author'];
- $item['author-id'] = Contact::getIdForURL($activity['author'], 0, true);
- $item['owner-link'] = $activity['actor'];
- $item['owner-id'] = Contact::getIdForURL($activity['actor'], 0, true);
-
- if (in_array(0, $activity['receiver']) && !empty($activity['unlisted'])) {
- $item['private'] = Item::UNLISTED;
- } elseif (in_array(0, $activity['receiver'])) {
- $item['private'] = Item::PUBLIC;
- } else {
- $item['private'] = Item::PRIVATE;
- }
-
- if (!empty($activity['raw'])) {
- $item['source'] = $activity['raw'];
- $item['protocol'] = Conversation::PARCEL_ACTIVITYPUB;
- $item['conversation-href'] = $activity['context'] ?? '';
- $item['conversation-uri'] = $activity['conversation'] ?? '';
-
- if (isset($activity['push'])) {
- $item['direction'] = $activity['push'] ? Conversation::PUSH : Conversation::PULL;
- }
- }
-
- $isForum = false;
-
- if (!empty($activity['thread-completion'])) {
- // Store the original actor in the "causer" fields to enable the check for ignored or blocked contacts
- $item['causer-link'] = $item['owner-link'];
- $item['causer-id'] = $item['owner-id'];
-
- Logger::info('Ignoring actor because of thread completion.', ['actor' => $item['owner-link']]);
- $item['owner-link'] = $item['author-link'];
- $item['owner-id'] = $item['author-id'];
- } else {
- $actor = APContact::getByURL($item['owner-link'], false);
- $isForum = ($actor['type'] == 'Group');
- }
-
- $item['uri'] = $activity['id'];
-
- $item['created'] = DateTimeFormat::utc($activity['published']);
- $item['edited'] = DateTimeFormat::utc($activity['updated']);
- $guid = $activity['sc:identifier'] ?: self::getGUIDByURL($item['uri']);
- $item['guid'] = $activity['diaspora:guid'] ?: $guid;
-
- $item['uri-id'] = ItemURI::insert(['uri' => $item['uri'], 'guid' => $item['guid']]);
-
- $item = self::processContent($activity, $item);
if (empty($item)) {
return;
}
- $item['plink'] = $activity['alternate-url'] ?? $item['uri'];
-
- $item = self::constructAttachList($activity, $item);
-
$stored = false;
foreach ($activity['receiver'] as $receiver) {
$item['uid'] = $receiver;
- if ($isForum) {
- $item['contact-id'] = Contact::getIdForURL($activity['actor'], $receiver, true);
+ $type = $activity['reception_type'][$receiver] ?? Receiver::TARGET_UNKNOWN;
+ switch($type) {
+ case Receiver::TARGET_TO:
+ $item['post-type'] = Item::PT_TO;
+ break;
+ case Receiver::TARGET_CC:
+ $item['post-type'] = Item::PT_CC;
+ break;
+ case Receiver::TARGET_BTO:
+ $item['post-type'] = Item::PT_BTO;
+ break;
+ case Receiver::TARGET_BCC:
+ $item['post-type'] = Item::PT_BCC;
+ break;
+ case Receiver::TARGET_FOLLOWER:
+ $item['post-type'] = Item::PT_FOLLOWER;
+ break;
+ case Receiver::TARGET_ANSWER:
+ $item['post-type'] = Item::PT_COMMENT;
+ break;
+ case Receiver::TARGET_GLOBAL:
+ $item['post-type'] = Item::PT_GLOBAL;
+ break;
+ default:
+ $item['post-type'] = Item::PT_ARTICLE;
+ }
+
+ if (in_array($item['post-type'], [Item::PT_COMMENT, Item::PT_GLOBAL, Item::PT_ARTICLE])) {
+ if (!empty($activity['from-relay'])) {
+ $item['post-type'] = Item::PT_RELAY;
+ } elseif (!empty($activity['thread-completion'])) {
+ $item['post-type'] = Item::PT_FETCHED;
+ }
+ }
+
+ if (!empty($activity['from-relay'])) {
+ $item['causer-id'] = $activity['from-relay'];
+ }
+
+ if ($item['isForum'] ?? false) {
+ $item['contact-id'] = Contact::getIdForURL($activity['actor'], $receiver);
} else {
- $item['contact-id'] = Contact::getIdForURL($activity['author'], $receiver, true);
+ $item['contact-id'] = Contact::getIdForURL($activity['author'], $receiver);
}
if (($receiver != 0) && empty($item['contact-id'])) {
- $item['contact-id'] = Contact::getIdForURL($activity['author'], 0, true);
+ $item['contact-id'] = Contact::getIdForURL($activity['author']);
}
if (!empty($activity['directmessage'])) {
if (DI::pConfig()->get($receiver, 'system', 'accept_only_sharer', false) && ($receiver != 0) && ($item['gravity'] == GRAVITY_PARENT)) {
$skip = !Contact::isSharingByURL($activity['author'], $receiver);
- if ($skip && (($activity['type'] == 'as:Announce') || $isForum)) {
+ if ($skip && (($activity['type'] == 'as:Announce') || ($item['isForum'] ?? false))) {
$skip = !Contact::isSharingByURL($activity['actor'], $receiver);
}
/**
* Fetches missing posts
*
- * @param string $url message URL
- * @param array $child activity array with the child of this message
+ * @param string $url message URL
+ * @param array $child activity array with the child of this message
+ * @param string $relay_actor Relay actor
* @return string fetched message URL
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
- public static function fetchMissingActivity($url, $child = [])
+ public static function fetchMissingActivity(string $url, array $child = [], string $relay_actor = '')
{
if (!empty($child['receiver'])) {
$uid = ActivityPub\Receiver::getFirstUserFromReceivers($child['receiver']);
return '';
}
- if (!empty($child['author'])) {
- $actor = $child['author'];
- } elseif (!empty($object['actor'])) {
- $actor = $object['actor'];
+ if (!empty($object['actor'])) {
+ $object_actor = $object['actor'];
} elseif (!empty($object['attributedTo'])) {
- $actor = $object['attributedTo'];
+ $object_actor = $object['attributedTo'];
} else {
// Shouldn't happen
- $actor = '';
+ $object_actor = '';
+ }
+
+ $signer = [$object_actor];
+
+ if (!empty($child['author'])) {
+ $actor = $child['author'];
+ $signer[] = $actor;
+ } else {
+ $actor = $object_actor;
}
if (!empty($object['published'])) {
$ldactivity = JsonLD::compact($activity);
$ldactivity['thread-completion'] = true;
+ $ldactivity['from-relay'] = Contact::getIdForURL($relay_actor);
- ActivityPub\Receiver::processActivity($ldactivity, json_encode($activity));
+ ActivityPub\Receiver::processActivity($ldactivity, json_encode($activity), $uid, true, false, $signer);
Logger::notice('Activity had been fetched and processed.', ['url' => $url, 'object' => $activity['id']]);
}
$owner = User::getOwnerDataById($uid);
+ if (empty($owner)) {
+ return;
+ }
$cid = Contact::getIdForURL($activity['actor'], $uid);
if (!empty($cid)) {
}
Logger::info('Updating profile', ['object' => $activity['object_id']]);
- Contact::updateFromProbeByURL($activity['object_id'], true);
+ Contact::updateFromProbeByURL($activity['object_id']);
}
/**
}
$owner = User::getOwnerDataById($uid);
+ if (empty($owner)) {
+ return;
+ }
$cid = Contact::getIdForURL($activity['actor'], $uid);
if (empty($cid)) {
{
$parent_terms = Tag::getByURIId($parent['uri-id'], [Tag::MENTION, Tag::IMPLICIT_MENTION, Tag::EXCLUSIVE_MENTION]);
- $parent_author = Contact::getByURL($parent['author-link'], 0, ['url', 'nurl', 'alias'], false);
+ $parent_author = Contact::getByURL($parent['author-link'], false, ['url', 'nurl', 'alias']);
$implicit_mentions = [];
if (empty($parent_author['url'])) {
}
foreach ($parent_terms as $term) {
- $contact = Contact::getByURL($term['url'], 0, ['url', 'nurl', 'alias'], false);
+ $contact = Contact::getByURL($term['url'], false, ['url', 'nurl', 'alias']);
if (!empty($contact['url'])) {
$implicit_mentions[] = $contact['url'];
$implicit_mentions[] = $contact['nurl'];