diff --git a/src/Content/Text/BBCode.php b/src/Content/Text/BBCode.php index c2b54821aa..38719e046c 100644 --- a/src/Content/Text/BBCode.php +++ b/src/Content/Text/BBCode.php @@ -1052,9 +1052,12 @@ class BBCode extends BaseObject $text = ($is_quote_share? '
' : '') . '

' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' ' . $author_contact['addr'] . ':

' . "\n" . $content; break; case 7: // statusnet/GNU Social - case 9: // ActivityPub $text = ($is_quote_share? '
' : '') . '

' . html_entity_decode('♲ ', ENT_QUOTES, 'UTF-8') . ' @' . $author_contact['addr'] . ': ' . $content . '

' . "\n"; break; + case 9: // ActivityPub + $author = '@' . $author_contact['addr'] . ':'; + $text = '
' . html_entity_decode('♲', ENT_QUOTES, 'UTF-8') . ' ' . $author . '
' . $content . '
' . "\n"; + break; default: // Transforms quoted tweets in rich attachments to avoid nested tweets if (stripos(Strings::normaliseLink($attributes['link']), 'http://twitter.com/') === 0 && OEmbed::isAllowedURL($attributes['link'])) { diff --git a/src/Module/Objects.php b/src/Module/Objects.php index 5538be1888..df57636926 100644 --- a/src/Module/Objects.php +++ b/src/Module/Objects.php @@ -42,7 +42,14 @@ class Objects extends BaseModule } } - $data = ActivityPub\Transmitter::createObjectFromItemID($item['id']); + $activity = ActivityPub\Transmitter::createActivityFromItem($item['id'], true); + // Only display "Create" activity objects here, no reshares or anything else + if (!is_array($activity['object']) || ($activity['type'] != 'Create')) { + throw new \Friendica\Network\HTTPException\NotFoundException(); + } + + $data = ['@context' => ActivityPub::CONTEXT]; + $data = array_merge($data, $activity['object']); header('Content-Type: application/activity+json'); echo json_encode($data); diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index fa8e4bf46f..a9f5ed20a1 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -176,9 +176,11 @@ class Transmitter $items = Item::select(['id'], $condition, ['limit' => [($page - 1) * 20, 20], 'order' => ['created' => true]]); while ($item = Item::fetch($items)) { - $object = self::createObjectFromItemID($item['id']); - unset($object['@context']); - $list[] = $object; + $activity = self::createActivityFromItem($item['id'], true); + // Only list "Create" activity objects here, no reshares + if (is_array($activity['object']) && ($activity['type'] == 'Create')) { + $list[] = $activity['object']; + } } if (!empty($list)) { @@ -379,6 +381,15 @@ class Transmitter $terms = Term::tagArrayFromItemId($item['id'], [Term::MENTION, Term::IMPLICIT_MENTION]); + // Directly mention the original author upon a quoted reshare. + // Else just ensure that the original author receives the reshare. + $announce = self::getAnnounceArray($item); + if (!empty($announce['comment'])) { + $data['to'][] = $announce['actor']['url']; + } elseif (!empty($announce)) { + $data['cc'][] = $announce['actor']['url']; + } + if (!$item['private']) { $data = array_merge($data, self::fetchPermissionBlockFromConversation($item)); @@ -757,8 +768,8 @@ class Transmitter // Only check for a reshare, if it is a real reshare and no quoted reshare if (strpos($item['body'], "[share") === 0) { - $announce = api_share_as_retweet($item); - $reshared = !empty($announce['plink']); + $announce = self::getAnnounceArray($item); + $reshared = !empty($announce); } if ($reshared) { @@ -919,29 +930,6 @@ class Transmitter /// @todo Create "conversation" entry } - /** - * Creates an object array for a given item id - * - * @param integer $item_id - * - * @return array with the object data - * @throws \Friendica\Network\HTTPException\InternalServerErrorException - * @throws \ImagickException - */ - public static function createObjectFromItemID($item_id) - { - $item = Item::selectFirst([], ['id' => $item_id, 'parent-network' => Protocol::NATIVE_SUPPORT]); - - if (!DBA::isResult($item)) { - return false; - } - - $data = ['@context' => ActivityPub::CONTEXT]; - $data = array_merge($data, self::createNote($item)); - - return $data; - } - /** * Creates a location entry for a given item array * @@ -1004,6 +992,13 @@ class Transmitter $tags[] = ['type' => 'Mention', 'href' => $term['url'], 'name' => $mention]; } } + + $announce = self::getAnnounceArray($item); + // Mention the original author upon commented reshares + if (!empty($announce['comment'])) { + $tags[] = ['type' => 'Mention', 'href' => $announce['actor']['url'], 'name' => '@' . $announce['actor']['addr']]; + } + return $tags; } @@ -1371,46 +1366,74 @@ class Transmitter private static function createAnnounce($item, $data) { $orig_body = $item['body']; - $announce = api_share_as_retweet($item); - if (empty($announce['plink'])) { + $announce = self::getAnnounceArray($item); + if (empty($announce)) { $data['type'] = 'Create'; $data['object'] = self::createNote($item); return $data; } - // Fetch the original id of the object - $activity = ActivityPub::fetchContent($announce['plink'], $item['uid']); - if (!empty($activity)) { - $ldactivity = JsonLD::compact($activity); - $id = JsonLD::fetchElement($ldactivity, '@id'); - $type = str_replace('as:', '', JsonLD::fetchElement($ldactivity, '@type')); - if (!empty($id)) { - if (empty($announce['share-pre-body'])) { - // Pure announce, without a quote - $data['type'] = 'Announce'; - $data['object'] = $id; - return $data; - } - - // Quote - $data['type'] = 'Create'; - $item['body'] = trim($announce['share-pre-body']) . "\n" . $id; - $data['object'] = self::createNote($item); - - /// @todo Finally descide how to implement this in AP. This is a possible way: - $data['object']['attachment'][] = ['type' => $type, 'id' => $id]; - - $data['object']['source']['content'] = $orig_body; - return $data; - } + if (empty($announce['comment'])) { + // Pure announce, without a quote + $data['type'] = 'Announce'; + $data['object'] = $announce['object']['uri']; + return $data; } - $item['body'] = $orig_body; + // Quote $data['type'] = 'Create'; + $item['body'] = $announce['comment'] . "\n" . $announce['object']['plink']; $data['object'] = self::createNote($item); + + /// @todo Finally descide how to implement this in AP. This is a possible way: + $data['object']['attachment'][] = self::createNote($announce['object']); + + $data['object']['source']['content'] = $orig_body; return $data; } + /** + * Return announce related data if the item is an annunce + * + * @param array $item + * + * @return array + */ + public static function getAnnounceArray($item) + { + if (!preg_match("/(.*?)\[share(.*?)\]\s?.*?\s?\[\/share\]\s?/ism", $item['body'], $matches)) { + return []; + } + + $attributes = $matches[2]; + $comment = $matches[1]; + + preg_match("/guid='(.*?)'/ism", $attributes, $matches); + if (empty($matches[1])) { + preg_match('/guid="(.*?)"/ism', $attributes, $matches); + } + + if (empty($matches[1])) { + return []; + } + + $reshared_item = Item::selectFirst([], ['guid' => $matches[1]]); + if (!DBA::isResult($reshared_item)) { + return []; + } + + if (!in_array($reshared_item['network'], [Protocol::ACTIVITYPUB, Protocol::DFRN])) { + return []; + } + + $profile = APContact::getByURL($reshared_item['author-link'], false); + if (empty($profile)) { + return []; + } + + return ['object' => $reshared_item, 'actor' => $profile, 'comment' => trim($comment)]; + } + /** * Creates an activity id for a given contact id *