X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FProtocol%2FActivityPub%2FTransmitter.php;h=baaaba0680e45100177a048ea5f70e7c4aa5ddce;hb=065d73f860e40e5e3c0646a39edd30997d34f72e;hp=e7bb20893bc1f7d8d6b71dd7f93816ab58f5385b;hpb=a91e3f5dbce82471a04e9a3bf3a9957589f766be;p=friendica.git diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index e7bb20893b..baaaba0680 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -1,6 +1,6 @@ Contact::FRIEND], ['id' => $contact['id']]); + Contact::update(['rel' => Contact::FRIEND], ['id' => $contact['id']]); } return $success; @@ -137,7 +137,7 @@ class Transmitter $success = self::sendContactUndo($url, $contact['id'], 0); if ($success || $force) { - DBA::update('contact', ['rel' => Contact::NOTHING], ['id' => $contact['id']]); + Contact::update(['rel' => Contact::NOTHING], ['id' => $contact['id']]); } return $success; @@ -146,15 +146,16 @@ class Transmitter /** * Collects a list of contacts of the given owner * - * @param array $owner Owner array - * @param int|array $rel The relevant value(s) contact.rel should match - * @param string $module The name of the relevant AP endpoint module (followers|following) - * @param integer $page Page number + * @param array $owner Owner array + * @param array $rel The relevant value(s) contact.rel should match + * @param string $module The name of the relevant AP endpoint module (followers|following) + * @param integer $page Page number + * @param string $requester URL of the requester * * @return array of owners * @throws \Exception */ - public static function getContacts($owner, $rel, $module, $page = null) + public static function getContacts(array $owner, array $rel, string $module, int $page = null, string $requester = null) { $parameters = [ 'rel' => $rel, @@ -166,25 +167,31 @@ class Transmitter 'pending' => false, 'blocked' => false, ]; - $condition = DBA::buildCondition($parameters); - $sql = "SELECT COUNT(*) as `count` - FROM `contact` - JOIN `apcontact` ON `apcontact`.`url` = `contact`.`url` - " . $condition; + $condition = DBA::mergeConditions($parameters, ["`url` IN (SELECT `url` FROM `apcontact`)"]); - $contacts = DBA::fetchFirst($sql, ...$parameters); + $total = DBA::count('contact', $condition); $modulePath = '/' . $module . '/'; $data = ['@context' => ActivityPub::CONTEXT]; $data['id'] = DI::baseUrl() . $modulePath . $owner['nickname']; $data['type'] = 'OrderedCollection'; - $data['totalItems'] = $contacts['count']; + $data['totalItems'] = $total; + + if (!empty($page)) { + $data['id'] .= '?' . http_build_query(['page' => $page]); + } // When we hide our friends we will only show the pure number but don't allow more. - $profile = Profile::getByUID($owner['uid']); - if (!empty($profile['hide-friends'])) { + $show_contacts = empty($owner['hide-friends']); + + // Allow fetching the contact list when the requester is part of the list. + if (($owner['page-flags'] == User::PAGE_FLAGS_PRVGROUP) && !empty($requester)) { + $show_contacts = DBA::exists('contact', ['nurl' => Strings::normaliseLink($requester), 'uid' => $owner['uid'], 'blocked' => false]); + } + + if (!$show_contacts) { return $data; } @@ -194,22 +201,13 @@ class Transmitter $data['type'] = 'OrderedCollectionPage'; $list = []; - $sql = "SELECT `contact`.`url` - FROM `contact` - JOIN `apcontact` ON `apcontact`.`url` = `contact`.`url` - " . $condition . " - LIMIT ?, ?"; - - $parameters[] = ($page - 1) * 100; - $parameters[] = 100; - - $contacts = DBA::p($sql, ...$parameters); + $contacts = DBA::select('contact', ['url'], $condition, ['limit' => [($page - 1) * 100, 100]]); while ($contact = DBA::fetch($contacts)) { $list[] = $contact['url']; } DBA::close($contacts); - if (!empty($list)) { + if (count($list) == 100) { $data['next'] = DI::baseUrl() . $modulePath . $owner['nickname'] . '?page=' . ($page + 1); } @@ -232,7 +230,7 @@ class Transmitter * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function getOutbox($owner, $page = null, $requester = '') + public static function getOutbox(array $owner, int $page = null, string $requester = '') { $condition = ['private' => [Item::PUBLIC, Item::UNLISTED]]; @@ -242,7 +240,7 @@ class Transmitter $permissionSets = DI::permissionSet()->selectByContactId($requester_id, $owner['uid']); if (!empty($permissionSets)) { $condition = ['psid' => array_merge($permissionSets->column('id'), - [DI::permissionSet()->getIdFromACL($owner['uid'], '', '', '', '')])]; + [DI::permissionSet()->selectPublicForUser($owner['uid'])])]; } } } @@ -264,6 +262,10 @@ class Transmitter $data['type'] = 'OrderedCollection'; $data['totalItems'] = $count; + if (!empty($page)) { + $data['id'] .= '?' . http_build_query(['page' => $page]); + } + if (empty($page)) { $data['first'] = DI::baseUrl() . '/outbox/' . $owner['nickname'] . '?page=1'; } else { @@ -282,7 +284,7 @@ class Transmitter } DBA::close($items); - if (!empty($list)) { + if (count($list) == 20) { $data['next'] = DI::baseUrl() . '/outbox/' . $owner['nickname'] . '?page=' . ($page + 1); } @@ -294,6 +296,75 @@ class Transmitter return $data; } + /** + * Public posts for the given owner + * + * @param array $owner Owner array + * @param integer $page Page number + * + * @return array of posts + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public static function getFeatured(array $owner, int $page = null) + { + $condition = ["`uri-id` IN (SELECT `uri-id` FROM `collection-view` WHERE `cid` = ? AND `type` = ?)", + Contact::getIdForURL($owner['url'], 0, false), Post\Collection::FEATURED]; + + $condition = DBA::mergeConditions($condition, + ['uid' => $owner['uid'], + 'author-id' => Contact::getIdForURL($owner['url'], 0, false), + 'private' => [Item::PUBLIC, Item::UNLISTED], + 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], + 'network' => Protocol::FEDERATED, + 'parent-network' => Protocol::FEDERATED, + 'origin' => true, + 'deleted' => false, + 'visible' => true]); + + $count = Post::count($condition); + + $data = ['@context' => ActivityPub::CONTEXT]; + $data['id'] = DI::baseUrl() . '/featured/' . $owner['nickname']; + $data['type'] = 'OrderedCollection'; + $data['totalItems'] = $count; + + if (!empty($page)) { + $data['id'] .= '?' . http_build_query(['page' => $page]); + } + + if (empty($page)) { + $items = Post::select(['id'], $condition, ['limit' => 20, 'order' => ['created' => true]]); + } else { + $data['type'] = 'OrderedCollectionPage'; + $items = Post::select(['id'], $condition, ['limit' => [($page - 1) * 20, 20], 'order' => ['created' => true]]); + } + $list = []; + + while ($item = Post::fetch($items)) { + $activity = self::createActivityFromItem($item['id'], true); + $activity['type'] = $activity['type'] == 'Update' ? 'Create' : $activity['type']; + + // Only list "Create" activity objects here, no reshares + if (!empty($activity['object']) && ($activity['type'] == 'Create')) { + $list[] = $activity['object']; + } + } + DBA::close($items); + + if (count($list) == 20) { + $data['next'] = DI::baseUrl() . '/featured/' . $owner['nickname'] . '?page=' . ($page + 1); + } + + if (!empty($page)) { + $data['partOf'] = DI::baseUrl() . '/featured/' . $owner['nickname']; + } + + $data['orderedItems'] = $list; + + return $data; + } + /** * Return the service array containing information the used software and it's url * @@ -334,8 +405,9 @@ class Transmitter if ($uid != 0) { $data['following'] = DI::baseUrl() . '/following/' . $owner['nick']; $data['followers'] = DI::baseUrl() . '/followers/' . $owner['nick']; - $data['inbox'] = DI::baseUrl() . '/inbox/' . $owner['nick']; - $data['outbox'] = DI::baseUrl() . '/outbox/' . $owner['nick']; + $data['inbox'] = DI::baseUrl() . '/inbox/' . $owner['nick']; + $data['outbox'] = DI::baseUrl() . '/outbox/' . $owner['nick']; + $data['featured'] = DI::baseUrl() . '/featured/' . $owner['nick']; } else { $data['inbox'] = DI::baseUrl() . '/friendica/inbox'; } @@ -370,7 +442,7 @@ class Transmitter 'owner' => $owner['url'], 'publicKeyPem' => $owner['pubkey']]; $data['endpoints'] = ['sharedInbox' => DI::baseUrl() . '/inbox']; - $data['icon'] = ['type' => 'Image', 'url' => User::getAvatarUrlForId($uid)]; + $data['icon'] = ['type' => 'Image', 'url' => User::getAvatarUrl($owner)]; $resourceid = Photo::ridFromURI($owner['photo']); if (!empty($resourceid)) { @@ -430,35 +502,34 @@ class Transmitter } /** - * Returns an array with permissions of a given item array + * Returns an array with permissions of the thread parent of the given item array * * @param array $item + * @param bool $is_forum_thread * * @return array with permissions * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function fetchPermissionBlockFromConversation($item) + private static function fetchPermissionBlockFromThreadParent(array $item, bool $is_forum_thread) { - if (empty($item['thr-parent'])) { + if (empty($item['thr-parent-id'])) { return []; } - $condition = ['item-uri' => $item['thr-parent'], 'protocol' => Conversation::PARCEL_ACTIVITYPUB]; - $conversation = DBA::selectFirst('conversation', ['source'], $condition); - if (!DBA::isResult($conversation)) { + $parent = Post::selectFirstPost(['author-link'], ['uri-id' => $item['thr-parent-id']]); + if (empty($parent)) { return []; } - $activity = json_decode($conversation['source'], true); + $permissions = [ + 'to' => [$parent['author-link']], + 'cc' => [], + 'bto' => [], + 'bcc' => [], + ]; - $actor = JsonLD::fetchElement($activity, 'actor', 'id'); - if (!empty($actor)) { - $permissions['to'][] = $actor; - $profile = APContact::getByURL($actor); - } else { - $profile = []; - } + $parent_profile = APContact::getByURL($parent['author-link']); $item_profile = APContact::getByURL($item['author-link']); $exclude[] = $item['author-link']; @@ -467,26 +538,17 @@ class Transmitter $exclude[] = $item['owner-link']; } - foreach (['to', 'cc', 'bto', 'bcc'] as $element) { - if (empty($activity[$element])) { - continue; - } - if (is_string($activity[$element])) { - $activity[$element] = [$activity[$element]]; - } - - foreach ($activity[$element] as $receiver) { - if (empty($receiver)) { - continue; - } - - if (!empty($profile['followers']) && $receiver == $profile['followers'] && !empty($item_profile['followers'])) { - $permissions[$element][] = $item_profile['followers']; - } elseif (!in_array($receiver, $exclude)) { - $permissions[$element][] = $receiver; + $type = [Tag::TO => 'to', Tag::CC => 'cc', Tag::BTO => 'bto', Tag::BCC => 'bcc']; + foreach (Tag::getByURIId($item['thr-parent-id'], [Tag::TO, Tag::CC, Tag::BTO, Tag::BCC]) as $receiver) { + if (!empty($parent_profile['followers']) && $receiver['url'] == $parent_profile['followers'] && !empty($item_profile['followers'])) { + if (!$is_forum_thread) { + $permissions[$type[$receiver['type']]][] = $item_profile['followers']; } + } elseif (!in_array($receiver['url'], $exclude)) { + $permissions[$type[$receiver['type']]][] = $receiver['url']; } } + return $permissions; } @@ -508,28 +570,33 @@ class Transmitter /** * Creates an array of permissions from an item thread * - * @param array $item Item array - * @param boolean $blindcopy addressing via "bcc" or "cc"? - * @param integer $last_id Last item id for adding receivers - * @param boolean $forum_mode "true" means that we are sending content to a forum + * @param array $item Item array + * @param boolean $blindcopy addressing via "bcc" or "cc"? + * @param integer $last_id Last item id for adding receivers * * @return array with permission data * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - private static function createPermissionBlockForItem($item, $blindcopy, $last_id = 0, $forum_mode = false) + private static function createPermissionBlockForItem($item, $blindcopy, $last_id = 0) { if ($last_id == 0) { $last_id = $item['id']; } $always_bcc = false; + $is_forum = false; + $follower = ''; // Check if we should always deliver our stuff via BCC if (!empty($item['uid'])) { - $profile = User::getOwnerDataById($item['uid']); - if (!empty($profile)) { - $always_bcc = $profile['hide-friends']; + $owner = User::getOwnerDataById($item['uid']); + if (!empty($owner)) { + $always_bcc = $owner['hide-friends']; + $is_forum = ($owner['account-type'] == User::ACCOUNT_TYPE_COMMUNITY) && $owner['manually-approve']; + + $profile = APContact::getByURL($owner['url'], false); + $follower = $profile['followers'] ?? ''; } } @@ -537,6 +604,14 @@ class Transmitter $always_bcc = true; } + $parent = Post::selectFirst(['causer-link', 'post-reason'], ['id' => $item['parent']]); + if (($parent['post-reason'] == Item::PR_ANNOUNCEMENT) && !empty($parent['causer-link'])) { + $profile = APContact::getByURL($parent['causer-link'], false); + $is_forum_thread = isset($profile['type']) && $profile['type'] == 'Group'; + } else { + $is_forum_thread = false; + } + if (self::isAnnounce($item) || DI::config()->get('debug', 'total_ap_delivery') || self::isAPPost($last_id)) { // Will be activated in a later step $networks = Protocol::FEDERATED; @@ -567,7 +642,7 @@ class Transmitter $data['cc'][] = $announce['actor']['url']; } - $data = array_merge($data, self::fetchPermissionBlockFromConversation($item)); + $data = array_merge($data, self::fetchPermissionBlockFromThreadParent($item, $is_forum_thread)); // Check if the item is completely public or unlisted if ($item['private'] == Item::PUBLIC) { @@ -599,30 +674,41 @@ class Transmitter continue; } - if (!empty($profile = APContact::getByURL($contact['url'], false))) { + $profile = APContact::getByURL($term['url'], false); + if (!empty($profile)) { + if ($term['type'] == Tag::EXCLUSIVE_MENTION) { + $exclusive = true; + if (!empty($profile['followers']) && ($profile['type'] == 'Group')) { + $data['cc'][] = $profile['followers']; + } + } $data['to'][] = $profile['url']; } } } - foreach ($receiver_list as $receiver) { - $contact = DBA::selectFirst('contact', ['url', 'hidden', 'network', 'protocol', 'gsid'], ['id' => $receiver, 'network' => Protocol::FEDERATED]); - if (!DBA::isResult($contact) || !self::isAPContact($contact, $networks)) { - continue; - } + if ($is_forum && !$exclusive && !empty($follower)) { + $data['cc'][] = $follower; + } elseif (!$exclusive) { + foreach ($receiver_list as $receiver) { + $contact = DBA::selectFirst('contact', ['url', 'hidden', 'network', 'protocol', 'gsid'], ['id' => $receiver, 'network' => Protocol::FEDERATED]); + if (!DBA::isResult($contact) || !self::isAPContact($contact, $networks)) { + continue; + } - if (!empty($profile = APContact::getByURL($contact['url'], false))) { - if ($contact['hidden'] || $always_bcc) { - $data['bcc'][] = $profile['url']; - } else { - $data['cc'][] = $profile['url']; + if (!empty($profile = APContact::getByURL($contact['url'], false))) { + if ($contact['hidden'] || $always_bcc) { + $data['bcc'][] = $profile['url']; + } else { + $data['cc'][] = $profile['url']; + } } } } } if (!empty($item['parent'])) { - $parents = Post::select(['id', 'author-link', 'owner-link', 'gravity', 'uri'], ['parent' => $item['parent']]); + $parents = Post::select(['id', 'author-link', 'owner-link', 'gravity', 'uri'], ['parent' => $item['parent']], ['order' => ['id']]); while ($parent = Post::fetch($parents)) { if ($parent['gravity'] == GRAVITY_PARENT) { $profile = APContact::getByURL($parent['owner-link'], false); @@ -636,15 +722,13 @@ class Transmitter $data['to'][] = $profile['url']; } else { $data['cc'][] = $profile['url']; - if (($item['private'] != Item::PRIVATE) && !empty($actor_profile['followers'])) { + if (($item['private'] != Item::PRIVATE) && !empty($actor_profile['followers'])&& !$is_forum_thread) { $data['cc'][] = $actor_profile['followers']; } } - } elseif (!$exclusive) { + } elseif (!$exclusive && !$is_forum_thread) { // Public thread parent post always are directed to the followers. - // This mustn't be done by posts that are directed to forum servers via the exclusive mention. - // But possibly in that case we could add the "followers" collection of the forum to the message. - if (($item['private'] != Item::PRIVATE) && !$forum_mode) { + if ($item['private'] != Item::PRIVATE) { $data['cc'][] = $actor_profile['followers']; } } @@ -706,6 +790,19 @@ class Transmitter unset($receivers['bcc']); } + foreach (['to' => Tag::TO, 'cc' => Tag::CC, 'bcc' => Tag::BCC] as $element => $type) { + if (!empty($receivers[$element])) { + foreach ($receivers[$element] as $receiver) { + if ($receiver == ActivityPub::PUBLIC_COLLECTION) { + $name = Receiver::PUBLIC_COLLECTION; + } else { + $name = trim(parse_url($receiver, PHP_URL_PATH), '/'); + } + Tag::store($item['uri-id'], $type, $name, $receiver); + } + } + } + return $receivers; } @@ -810,18 +907,17 @@ class Transmitter /** * Fetches an array of inboxes for the given item and user * - * @param array $item Item array - * @param integer $uid User ID - * @param boolean $personal fetch personal inboxes - * @param integer $last_id Last item id for adding receivers - * @param boolean $forum_mode "true" means that we are sending content to a forum + * @param array $item Item array + * @param integer $uid User ID + * @param boolean $personal fetch personal inboxes + * @param integer $last_id Last item id for adding receivers * @return array with inboxes * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function fetchTargetInboxes($item, $uid, $personal = false, $last_id = 0, $forum_mode = false) + public static function fetchTargetInboxes($item, $uid, $personal = false, $last_id = 0) { - $permissions = self::createPermissionBlockForItem($item, true, $last_id, $forum_mode); + $permissions = self::createPermissionBlockForItem($item, true, $last_id); if (empty($permissions)) { return []; } @@ -904,6 +1000,7 @@ class Transmitter $mail['title'] = ''; } + $mail['content-warning'] = ''; $mail['author-link'] = $mail['owner-link'] = $mail['from-url']; $mail['owner-id'] = $mail['author-id']; $mail['allow_cid'] = '<'.$mail['contact-id'].'>'; @@ -1078,20 +1175,6 @@ class Transmitter return false; } - // In case of a forum post ensure to return the original post if author and forum are on the same machine - if (($item['gravity'] == GRAVITY_PARENT) && !empty($item['forum_mode'])) { - $author = Contact::getById($item['author-id'], ['nurl']); - if (!empty($author['nurl'])) { - $self = Contact::selectFirst(['uid'], ['nurl' => $author['nurl'], 'self' => true]); - if (!empty($self['uid'])) { - $forum_item = Post::selectFirst(Item::DELIVER_FIELDLIST, ['uri-id' => $item['uri-id'], 'uid' => $self['uid']]); - if (DBA::isResult($forum_item)) { - $item = $forum_item; - } - } - } - } - if (empty($item['uri-id'])) { Logger::warning('Item without uri-id', ['item' => $item]); return false; @@ -1160,6 +1243,7 @@ class Transmitter if (in_array($data['type'], ['Create', 'Update', 'Delete'])) { $data['object'] = $object ?? self::createNote($item); + $data['published'] = DateTimeFormat::utcNow(DateTimeFormat::ATOM); } elseif ($data['type'] == 'Add') { $data = self::createAddTag($item, $data); } elseif ($data['type'] == 'Announce') { @@ -1299,7 +1383,7 @@ class Transmitter $urls = []; foreach ($uriids as $uriid) { - foreach (Post\Media::getByURIId($uriid, [Post\Media::DOCUMENT, Post\Media::TORRENT]) as $attachment) { + foreach (Post\Media::getByURIId($uriid, [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO, Post\Media::DOCUMENT, Post\Media::TORRENT]) as $attachment) { if (in_array($attachment['url'], $urls)) { continue; } @@ -1326,52 +1410,6 @@ class Transmitter } } - if ($type != 'Note') { - return $attachments; - } - - foreach ($uriids as $uriid) { - foreach (Post\Media::getByURIId($uriid, [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO]) as $attachment) { - if (in_array($attachment['url'], $urls)) { - continue; - } - $urls[] = $attachment['url']; - - $attach = ['type' => 'Document', - 'mediaType' => $attachment['mimetype'], - 'url' => $attachment['url'], - 'name' => $attachment['description']]; - - if (!empty($attachment['height'])) { - $attach['height'] = $attachment['height']; - } - - if (!empty($attachment['width'])) { - $attach['width'] = $attachment['width']; - } - - if (!empty($attachment['preview'])) { - $attach['image'] = $attachment['preview']; - } - - $attachments[] = $attach; - } - // Currently deactivated, since it creates side effects on Mastodon and Pleroma. - // It will be activated, once this cleared. - /* - foreach (Post\Media::getByURIId($uriid, [Post\Media::HTML]) as $attachment) { - if (in_array($attachment['url'], $urls)) { - continue; - } - $urls[] = $attachment['url']; - - $attachments[] = ['type' => 'Page', - 'mediaType' => $attachment['mimetype'], - 'url' => $attachment['url'], - 'name' => $attachment['description']]; - }*/ - } - return $attachments; } @@ -1460,7 +1498,7 @@ class Transmitter */ private static function isSensitive($uri_id) { - return DBA::exists('tag-view', ['uri-id' => $uri_id, 'name' => 'nsfw']); + return DBA::exists('tag-view', ['uri-id' => $uri_id, 'name' => 'nsfw', 'type' => Tag::HASHTAG]); } /** @@ -1476,10 +1514,10 @@ class Transmitter $event = []; $event['name'] = $item['event-summary']; $event['content'] = BBCode::convertForUriId($item['uri-id'], $item['event-desc'], BBCode::ACTIVITYPUB); - $event['startTime'] = DateTimeFormat::utc($item['event-start'] . '+00:00', DateTimeFormat::ATOM); + $event['startTime'] = DateTimeFormat::utc($item['event-start'], 'c'); if (!$item['event-nofinish']) { - $event['endTime'] = DateTimeFormat::utc($item['event-finish'] . '+00:00', DateTimeFormat::ATOM); + $event['endTime'] = DateTimeFormat::utc($item['event-finish'], 'c'); } if (!empty($item['event-location'])) { @@ -1487,7 +1525,8 @@ class Transmitter $event['location'] = self::createLocation($item); } - $event['dfrn:adjust'] = (bool)$item['event-adjust']; + // 2021.12: Backward compatibility value, all the events now "adjust" to the viewer timezone + $event['dfrn:adjust'] = true; return $event; } @@ -1507,10 +1546,28 @@ class Transmitter return []; } + // We are treating posts differently when they are directed to a community. + // This is done to better support Lemmy. Most of the changes should work with other systems as well. + // But to not risk compatibility issues we currently perform the changes only for communities. + if ($item['gravity'] == GRAVITY_PARENT) { + $isCommunityPost = !empty(Tag::getByURIId($item['uri-id'], [Tag::EXCLUSIVE_MENTION])); + $links = Post\Media::getByURIId($item['uri-id'], [Post\Media::HTML]); + if ($isCommunityPost && (count($links) == 1)) { + $link = $links[0]['url']; + } + } else { + $isCommunityPost = false; + } + if ($item['event-type'] == 'event') { $type = 'Event'; } elseif (!empty($item['title'])) { - $type = 'Article'; + if (!$isCommunityPost || empty($link)) { + $type = 'Article'; + } else { + // "Page" is used by Lemmy for posts that contain an external link + $type = 'Page'; + } } else { $type = 'Note'; } @@ -1542,7 +1599,7 @@ class Transmitter $data['updated'] = DateTimeFormat::utc($item['edited'] . '+00:00', DateTimeFormat::ATOM); } - $data['url'] = $item['plink']; + $data['url'] = $link ?? $item['plink']; $data['attributedTo'] = $item['author-link']; $data['sensitive'] = self::isSensitive($item['uri-id']); $data['context'] = self::fetchContextURLForItem($item); @@ -1579,6 +1636,19 @@ class Transmitter if ($type == 'Event') { $data = array_merge($data, self::createEvent($item)); } else { + if ($isCommunityPost) { + // For community posts we remove the visible "!user@domain.tld". + // This improves the look at systems like Lemmy. + // Also in the future we should control the community delivery via other methods. + $body = preg_replace("/!\[url\=[^\[\]]*\][^\[\]]*\[\/url\]/ism", '', $body); + } + + if ($type == 'Page') { + // When we transmit "Page" posts we have to remove the attachment. + // The attachment contains the link that we already transmit in the "url" field. + $body = preg_replace("/\s*\[attachment .*?\].*?\[\/attachment\]\s*/ism", '', $body); + } + $body = BBCode::setMentionsToNicknames($body); $data['content'] = BBCode::convertForUriId($item['uri-id'], $body, BBCode::ACTIVITYPUB); @@ -1794,7 +1864,7 @@ class Transmitter { $owner = User::getOwnerDataById($uid); - $suggestion = DI::fsuggest()->getById($suggestion_id); + $suggestion = DI::fsuggest()->selectOneById($suggestion_id); $data = ['@context' => ActivityPub::CONTEXT, 'id' => DI::baseUrl() . '/activity/' . System::createGUID(), @@ -1808,7 +1878,7 @@ class Transmitter $signed = LDSignature::sign($data, $owner); - Logger::log('Deliver profile deletion for user ' . $uid . ' to ' . $inbox . ' via ActivityPub', Logger::DEBUG); + Logger::info('Deliver profile deletion for user ' . $uid . ' to ' . $inbox . ' via ActivityPub'); return HTTPSignature::transmit($signed, $inbox, $uid); } @@ -1837,7 +1907,7 @@ class Transmitter $signed = LDSignature::sign($data, $owner); - Logger::log('Deliver profile relocation for user ' . $uid . ' to ' . $inbox . ' via ActivityPub', Logger::DEBUG); + Logger::info('Deliver profile relocation for user ' . $uid . ' to ' . $inbox . ' via ActivityPub'); return HTTPSignature::transmit($signed, $inbox, $uid); } @@ -1876,7 +1946,7 @@ class Transmitter $signed = LDSignature::sign($data, $owner); - Logger::log('Deliver profile deletion for user ' . $uid . ' to ' . $inbox . ' via ActivityPub', Logger::DEBUG); + Logger::info('Deliver profile deletion for user ' . $uid . ' to ' . $inbox . ' via ActivityPub'); return HTTPSignature::transmit($signed, $inbox, $uid); } @@ -1908,7 +1978,7 @@ class Transmitter $signed = LDSignature::sign($data, $owner); - Logger::log('Deliver profile update for user ' . $uid . ' to ' . $inbox . ' via ActivityPub', Logger::DEBUG); + Logger::info('Deliver profile update for user ' . $uid . ' to ' . $inbox . ' via ActivityPub'); return HTTPSignature::transmit($signed, $inbox, $uid); } @@ -1945,7 +2015,7 @@ class Transmitter 'instrument' => self::getService(), 'to' => [$profile['url']]]; - Logger::log('Sending activity ' . $activity . ' to ' . $target . ' for user ' . $uid, Logger::DEBUG); + Logger::info('Sending activity ' . $activity . ' to ' . $target . ' for user ' . $uid); $signed = LDSignature::sign($data, $owner); return HTTPSignature::transmit($signed, $profile['inbox'], $uid); @@ -1984,7 +2054,7 @@ class Transmitter $condition = ['verb' => Activity::FOLLOW, 'uid' => 0, 'parent-uri' => $object, 'author-id' => Contact::getPublicIdByUserId($uid)]; if (Post::exists($condition)) { - Logger::log('Follow for ' . $object . ' for user ' . $uid . ' does already exist.', Logger::DEBUG); + Logger::info('Follow for ' . $object . ' for user ' . $uid . ' does already exist.'); return false; } @@ -1998,7 +2068,7 @@ class Transmitter 'instrument' => self::getService(), 'to' => [$profile['url']]]; - Logger::log('Sending follow ' . $object . ' to ' . $target . ' for user ' . $uid, Logger::DEBUG); + Logger::info('Sending follow ' . $object . ' to ' . $target . ' for user ' . $uid); $signed = LDSignature::sign($data, $owner); return HTTPSignature::transmit($signed, $profile['inbox'], $uid); @@ -2047,15 +2117,16 @@ class Transmitter * @param string $target Target profile * @param $id * @param integer $uid User ID - * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @return bool Operation success + * @throws HTTPException\InternalServerErrorException * @throws \ImagickException */ - public static function sendContactReject($target, $id, $uid) + public static function sendContactReject($target, $id, $uid): bool { $profile = APContact::getByURL($target); if (empty($profile['inbox'])) { Logger::warning('No inbox found for target', ['target' => $target, 'profile' => $profile]); - return; + return false; } $owner = User::getOwnerDataById($uid); @@ -2075,7 +2146,7 @@ class Transmitter Logger::debug('Sending reject to ' . $target . ' for user ' . $uid . ' with id ' . $id); $signed = LDSignature::sign($data, $owner); - HTTPSignature::transmit($signed, $profile['inbox'], $uid); + return HTTPSignature::transmit($signed, $profile['inbox'], $uid); } /** @@ -2114,7 +2185,7 @@ class Transmitter 'instrument' => self::getService(), 'to' => [$profile['url']]]; - Logger::log('Sending undo to ' . $target . ' for user ' . $uid . ' with id ' . $id, Logger::DEBUG); + Logger::info('Sending undo to ' . $target . ' for user ' . $uid . ' with id ' . $id); $signed = LDSignature::sign($data, $owner); return HTTPSignature::transmit($signed, $profile['inbox'], $uid);