From e4d28629e43f829877336f4207edd304069f9ed8 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 16 Sep 2018 17:47:00 +0000 Subject: [PATCH] First posting tests --- mod/follow.php | 4 +- src/Protocol/ActivityPub.php | 37 +++++++++------- src/Worker/Delivery.php | 82 ++++++++++++++++++++++++++++++++++++ src/Worker/Notifier.php | 2 +- 4 files changed, 107 insertions(+), 18 deletions(-) diff --git a/mod/follow.php b/mod/follow.php index ad1dd349cc..65028a70e0 100644 --- a/mod/follow.php +++ b/mod/follow.php @@ -31,8 +31,8 @@ function follow_post(App $a) // This is just a precaution if maybe this page is called somewhere directly via POST $_SESSION['fastlane'] = $url; -// $result = Contact::createFromProbe($uid, $url, true, Protocol::ACTIVITYPUB); - $result = Contact::createFromProbe($uid, $url, true); + $result = Contact::createFromProbe($uid, $url, true, Protocol::ACTIVITYPUB); +// $result = Contact::createFromProbe($uid, $url, true); if ($result['success'] == false) { if ($result['message']) { diff --git a/src/Protocol/ActivityPub.php b/src/Protocol/ActivityPub.php index 12477bad6b..ad27535ae6 100644 --- a/src/Protocol/ActivityPub.php +++ b/src/Protocol/ActivityPub.php @@ -95,8 +95,7 @@ class ActivityPub */ public static function profile($uid) { - $accounttype = ['Person', 'Organization', 'Service', 'Group', 'Application']; - + $accounttype = ['Person', 'Organization', 'Service', 'Group', 'Application', 'page-flags']; $condition = ['uid' => $uid, 'blocked' => false, 'account_expired' => false, 'account_removed' => false, 'verified' => true]; $fields = ['guid', 'nickname', 'pubkey', 'account-type']; @@ -105,7 +104,7 @@ class ActivityPub return []; } - $fields = ['locality', 'region', 'country-name', 'page-flags']; + $fields = ['locality', 'region', 'country-name']; $profile = DBA::selectFirst('profile', $fields, ['uid' => $uid, 'is-default' => true]); if (!DBA::isResult($profile)) { return []; @@ -119,6 +118,7 @@ class ActivityPub $data = ['@context' => ['https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1', ['uuid' => 'http://schema.org/identifier', 'sensitive' => 'as:sensitive', + 'manuallyApprovesFollowers' => 'as:manuallyApprovesFollowers', 'vcard' => 'http://www.w3.org/2006/vcard/ns#']]]; $data['id'] = $contact['url']; @@ -130,7 +130,7 @@ class ActivityPub $data['outbox'] = System::baseUrl() . '/outbox/' . $user['nickname']; $data['preferredUsername'] = $user['nickname']; $data['name'] = $contact['name']; - $data['vcard:hasAddress'] = ['@type' => 'Home', 'vcard:country-name' => $profile['country-name'], + $data['vcard:hasAddress'] = ['@type' => 'vcard:Home', 'vcard:country-name' => $profile['country-name'], 'vcard:region' => $profile['region'], 'vcard:locality' => $profile['locality']]; $data['summary'] = $contact['about']; $data['url'] = $contact['url']; @@ -183,9 +183,8 @@ class ActivityPub $data['context'] = $data['conversation'] = $conversation_uri; $data['actor'] = $item['author-link']; - $data['to'] = []; if (!$item['private']) { - $data['to'][] = 'https://www.w3.org/ns/activitystreams#Public'; + $data['to'] = 'https://www.w3.org/ns/activitystreams#Public'; } $data['published'] = DateTimeFormat::utc($item["created"]."+00:00", DateTimeFormat::ATOM); $data['updated'] = DateTimeFormat::utc($item["edited"]."+00:00", DateTimeFormat::ATOM); @@ -606,7 +605,7 @@ class ActivityPub // When it is a delivery to a personal inbox we add that user to the receivers if (!empty($uid)) { $owner = User::getOwnerDataById($uid); - $additional = [$owner['url'] => $uid]; + $additional = ['uid:' . $uid => $uid]; $receivers = array_merge($receivers, $additional); } @@ -738,10 +737,15 @@ class ActivityPub { $receivers = []; - $data = self::fetchContent($actor); - $followers = defaults($data, 'followers', ''); + if (!empty($actor)) { + $data = self::fetchContent($actor); + $followers = defaults($data, 'followers', ''); - logger('Actor: ' . $actor . ' - Followers: ' . $followers, LOGGER_DEBUG); + logger('Actor: ' . $actor . ' - Followers: ' . $followers, LOGGER_DEBUG); + } else { + logger('Empty actor', LOGGER_DEBUG); + $followers = ''; + } $elements = ['to', 'cc', 'bto', 'bcc']; foreach ($elements as $element) { @@ -757,7 +761,9 @@ class ActivityPub foreach ($activity[$element] as $receiver) { if ($receiver == self::PUBLIC) { $receivers['uid:0'] = 0; + } + if (($receiver == self::PUBLIC) && !empty($actor)) { // This will most likely catch all OStatus connections to Mastodon $condition = ['alias' => [$actor, normalise_link($actor)], 'rel' => [Contact::SHARING, Contact::FRIEND]]; $contacts = DBA::select('contact', ['uid'], $condition); @@ -769,7 +775,7 @@ class ActivityPub DBA::close($contacts); } - if (in_array($receiver, [$followers, self::PUBLIC])) { + if (in_array($receiver, [$followers, self::PUBLIC]) && !empty($actor)) { $condition = ['nurl' => normalise_link($actor), 'rel' => [Contact::SHARING, Contact::FRIEND], 'network' => Protocol::ACTIVITYPUB]; $contacts = DBA::select('contact', ['uid'], $condition); @@ -787,7 +793,7 @@ class ActivityPub if (!DBA::isResult($contact)) { continue; } - $receivers['cid:' . $contact['uid']] = $contact['uid']; + $receivers['uid:' . $contact['uid']] = $contact['uid']; } } return $receivers; @@ -820,6 +826,7 @@ class ActivityPub if (empty($data)) { logger('Empty content for ' . $object_url . ', check if content is available locally.', LOGGER_DEBUG); $data = $object_url; + $data = $object; } } else { logger('Using original object for url ' . $object_url, LOGGER_DEBUG); @@ -1063,7 +1070,7 @@ class ActivityPub if (($activity['uri'] != $activity['reply-to-uri']) && !Item::exists(['uri' => $activity['reply-to-uri']])) { logger('Parent ' . $activity['reply-to-uri'] . ' not found. Try to refetch it.'); - self::fetchMissingActivity($activity['reply-to-uri']); + self::fetchMissingActivity($activity['reply-to-uri'], $activity); } self::postItem($activity, $item, $body); @@ -1124,7 +1131,7 @@ class ActivityPub } } - private static function fetchMissingActivity($url) + private static function fetchMissingActivity($url, $child) { $object = ActivityPub::fetchContent($url); if (empty($object)) { @@ -1138,7 +1145,7 @@ class ActivityPub $activity['id'] = $object['id']; $activity['to'] = defaults($object, 'to', []); $activity['cc'] = defaults($object, 'cc', []); - $activity['actor'] = $object['attributedTo']; + $activity['actor'] = $activity['author']; $activity['object'] = $object; $activity['published'] = $object['published']; $activity['type'] = 'Create'; diff --git a/src/Worker/Delivery.php b/src/Worker/Delivery.php index e0a5c09c27..ef3a34649d 100644 --- a/src/Worker/Delivery.php +++ b/src/Worker/Delivery.php @@ -15,6 +15,7 @@ use Friendica\Model\Item; use Friendica\Model\Queue; use Friendica\Model\User; use Friendica\Protocol\DFRN; +use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Diaspora; use Friendica\Protocol\Email; @@ -165,6 +166,10 @@ class Delivery extends BaseObject switch ($contact['network']) { + case Protocol::ACTIVITYPUB: + self::deliverActivityPub($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup); + break; + case Protocol::DFRN: self::deliverDFRN($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup); break; @@ -383,6 +388,83 @@ class Delivery extends BaseObject logger('Unknown mode ' . $cmd . ' for ' . $loc); } + /** + * @brief Deliver content via ActivityPub +q * + * @param string $cmd Command + * @param array $contact Contact record of the receiver + * @param array $owner Owner record of the sender + * @param array $items Item record of the content and the parent + * @param array $target_item Item record of the content + * @param boolean $public_message Is the content public? + * @param boolean $top_level Is it a thread starter? + * @param boolean $followup Is it an answer to a remote post? + */ + private static function deliverActivityPub($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup) + { + // We don't treat Forum posts as "wall-to-wall" to be able to post them via ActivityPub + $walltowall = $top_level && ($owner['id'] != $items[0]['contact-id']) & ($owner['account-type'] != Contact::ACCOUNT_TYPE_COMMUNITY); + + if ($public_message) { + $loc = 'public batch ' . $contact['batch']; + } else { + $loc = $contact['addr']; + } + + logger('Deliver ' . $target_item["guid"] . ' via ActivityPub to ' . $loc); + +// if (Config::get('system', 'dfrn_only') || !Config::get('system', 'diaspora_enabled')) { +// return; +// } + if ($cmd == self::MAIL) { +// ActivityPub::sendMail($target_item, $owner, $contact); + return; + } + + if ($cmd == self::SUGGESTION) { + return; + } +// if (!$contact['pubkey'] && !$public_message) { +// logger('No public key, no delivery.'); +// return; +// } + if (($target_item['deleted']) && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) { + // top-level retraction + logger('ActivityPub retract: ' . $loc); +// ActivityPub::sendRetraction($target_item, $owner, $contact, $public_message); + return; + } elseif ($cmd == self::RELOCATION) { +// ActivityPub::sendAccountMigration($owner, $contact, $owner['uid']); + return; + } elseif ($followup) { + // send comments and likes to owner to relay + logger('ActivityPub followup: ' . $loc); + $data = ActivityPub::createActivityFromItem($target_item['id']); + $content = json_encode($data); + ActivityPub::transmit($content, $contact['notify'], $owner['uid']); +// ActivityPub::sendFollowup($target_item, $owner, $contact, $public_message); + return; + } elseif ($target_item['uri'] !== $target_item['parent-uri']) { + // we are the relay - send comments, likes and relayable_retractions to our conversants + logger('ActivityPub relay: ' . $loc); + $data = ActivityPub::createActivityFromItem($target_item['id']); + $content = json_encode($data); + ActivityPub::transmit($content, $contact['notify'], $owner['uid']); +// ActivityPub::sendRelay($target_item, $owner, $contact, $public_message); + return; + } elseif ($top_level && !$walltowall) { + // currently no workable solution for sending walltowall + logger('ActivityPub status: ' . $loc); + $data = ActivityPub::createActivityFromItem($target_item['id']); + $content = json_encode($data); + ActivityPub::transmit($content, $contact['notify'], $owner['uid']); +// ActivityPub::sendStatus($target_item, $owner, $contact, $public_message); + return; + } + + logger('Unknown mode ' . $cmd . ' for ' . $loc); + } + /** * @brief Deliver content via mail * diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 55f80c94d7..6a37186186 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -448,7 +448,7 @@ class Notifier } } - $condition = ['network' => Protocol::DFRN, 'uid' => $owner['uid'], 'blocked' => false, + $condition = ['network' => [Protocol::DFRN, Protocol::ACTIVITYPUB], 'uid' => $owner['uid'], 'blocked' => false, 'pending' => false, 'archive' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]]; $r2 = DBA::toArray(DBA::select('contact', ['id', 'name', 'network'], $condition));