X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FProtocol%2FActivityPub%2FTransmitter.php;h=7f22e8386b88c80130b5e15432edb522b29405af;hb=a1919992b998aaa4f0965acf4193574def2f7975;hp=77688233a8fd84412e3a2cb5da36beee8a01edaf;hpb=8dc1d19381d2974e2c99efff6a654aa8c24239ae;p=friendica.git diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 77688233a8..7f22e8386b 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -4,6 +4,7 @@ */ namespace Friendica\Protocol\ActivityPub; +use Friendica\BaseObject; use Friendica\Database\DBA; use Friendica\Core\System; use Friendica\Util\HTTPSignature; @@ -22,7 +23,11 @@ use Friendica\Model\Profile; use Friendica\Core\Config; use Friendica\Object\Image; use Friendica\Protocol\ActivityPub; +use Friendica\Protocol\Diaspora; use Friendica\Core\Cache; +use Friendica\Util\Map; + +require_once 'include/api.php'; /** * @brief ActivityPub Transmitter Protocol class @@ -33,7 +38,6 @@ use Friendica\Core\Cache; * - Event * * Complicated object types: - * - Announce * - Undo Announce * * General: @@ -42,7 +46,7 @@ use Friendica\Core\Cache; class Transmitter { /** - * @brief collects the lost of followers of the given owner + * collects the lost of followers of the given owner * * @param array $owner Owner array * @param integer $page Page number @@ -89,7 +93,7 @@ class Transmitter } /** - * @brief Create list of following contacts + * Create list of following contacts * * @param array $owner Owner array * @param integer $page Page numbe @@ -136,7 +140,7 @@ class Transmitter } /** - * @brief Public posts for the given owner + * Public posts for the given owner * * @param array $owner Owner array * @param integer $page Page numbe @@ -147,8 +151,8 @@ class Transmitter { $public_contact = Contact::getIdForURL($owner['url'], 0, true); - $condition = ['uid' => $owner['uid'], 'contact-id' => $owner['id'], 'author-id' => $public_contact, - 'wall' => true, 'private' => false, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], + $condition = ['uid' => 0, 'contact-id' => $public_contact, 'author-id' => $public_contact, + 'private' => false, 'gravity' => [GRAVITY_PARENT, GRAVITY_COMMENT], 'deleted' => false, 'visible' => true]; $count = DBA::count('item', $condition); @@ -205,12 +209,17 @@ class Transmitter return []; } - $fields = ['name', 'url', 'location', 'about', 'avatar']; + $fields = ['name', 'url', 'location', 'about', 'avatar', 'photo']; $contact = DBA::selectFirst('contact', $fields, ['uid' => $uid, 'self' => true]); if (!DBA::isResult($contact)) { return []; } + // On old installations and never changed contacts this might not be filled + if (empty($contact['avatar'])) { + $contact['avatar'] = $contact['photo']; + } + $data = ['@context' => ActivityPub::CONTEXT]; $data['id'] = $contact['url']; $data['diaspora:guid'] = $user['guid']; @@ -238,7 +247,7 @@ class Transmitter } /** - * @brief Returns an array with permissions of a given item array + * Returns an array with permissions of a given item array * * @param array $item * @@ -291,7 +300,7 @@ class Transmitter } /** - * @brief Creates an array of permissions from an item thread + * Creates an array of permissions from an item thread * * @param array $item * @@ -318,8 +327,12 @@ class Transmitter foreach ($terms as $term) { $profile = APContact::getByURL($term['url'], false); if (!empty($profile) && empty($contacts[$profile['url']])) { - $data['cc'][] = $profile['url']; + $data['to'][] = $profile['url']; $contacts[$profile['url']] = $profile['url']; + + if (($key = array_search($profile['url'], $data['cc'])) !== false) { + unset($data['cc'][$key]); + } } } } else { @@ -333,19 +346,23 @@ class Transmitter $contact = DBA::selectFirst('contact', ['url'], ['id' => $cid, 'network' => Protocol::ACTIVITYPUB]); $data['to'][] = $contact['url']; $contacts[$contact['url']] = $contact['url']; + + if (($key = array_search($profile['url'], $data['cc'])) !== false) { + unset($data['cc'][$key]); + } } } foreach ($receiver_list as $receiver) { $contact = DBA::selectFirst('contact', ['url'], ['id' => $receiver, 'network' => Protocol::ACTIVITYPUB]); - if (empty($contacts[$contact['url']])) { + if (DBA::isResult($contact) && empty($contacts[$contact['url']])) { $data['cc'][] = $contact['url']; $contacts[$contact['url']] = $contact['url']; } } } - $parents = Item::select(['id', 'author-link', 'owner-link', 'gravity'], ['parent' => $item['parent']]); + $parents = Item::select(['id', 'author-link', 'owner-link', 'gravity', 'uri'], ['parent' => $item['parent']]); while ($parent = Item::fetch($parents)) { // Don't include data from future posts if ($parent['id'] >= $item['id']) { @@ -353,6 +370,15 @@ class Transmitter } $profile = APContact::getByURL($parent['author-link'], false); + if (!empty($profile) && ($parent['uri'] == $item['thr-parent'])) { + $data['to'][] = $profile['url']; + $contacts[$profile['url']] = $profile['url']; + + if (($key = array_search($profile['url'], $data['cc'])) !== false) { + unset($data['cc'][$key]); + } + } + if (!empty($profile) && empty($contacts[$profile['url']])) { $data['cc'][] = $profile['url']; $contacts[$profile['url']] = $profile['url']; @@ -370,16 +396,19 @@ class Transmitter } DBA::close($parents); - if (empty($data['to'])) { - $data['to'] = $data['cc']; - $data['cc'] = []; + if (($key = array_search($item['author-link'], $data['to'])) !== false) { + unset($data['to'][$key]); } - return $data; + if (($key = array_search($item['author-link'], $data['cc'])) !== false) { + unset($data['cc'][$key]); + } + + return ['to' => array_values(array_unique($data['to'])), 'cc' => array_values(array_unique($data['cc']))]; } /** - * @brief Fetches a list of inboxes of followers of a given user + * Fetches a list of inboxes of followers of a given user * * @param integer $uid User ID * @@ -406,7 +435,7 @@ class Transmitter } /** - * @brief Fetches an array of inboxes for the given item and user + * Fetches an array of inboxes for the given item and user * * @param array $item * @param integer $uid User ID @@ -450,7 +479,7 @@ class Transmitter } /** - * @brief Returns the activity type of a given item + * Returns the activity type of a given item * * @param array $item * @@ -458,7 +487,9 @@ class Transmitter */ private static function getTypeOfItem($item) { - if ($item['verb'] == ACTIVITY_POST) { + if (!empty(Diaspora::isReshare($item['body'], false))) { + $type = 'Announce'; + } elseif ($item['verb'] == ACTIVITY_POST) { if ($item['created'] == $item['edited']) { $type = 'Create'; } else { @@ -482,7 +513,7 @@ class Transmitter } /** - * @brief Creates the activity or fetches it from the cache + * Creates the activity or fetches it from the cache * * @param integer $item_id * @@ -490,7 +521,7 @@ class Transmitter */ public static function createCachedActivityFromItem($item_id) { - $cachekey = "APDelivery:createActivity:".$item_id; + $cachekey = 'APDelivery:createActivity:' . $item_id; $data = Cache::get($cachekey); if (!is_null($data)) { return $data; @@ -503,7 +534,7 @@ class Transmitter } /** - * @brief Creates an activity array for a given item id + * Creates an activity array for a given item id * * @param integer $item_id * @param boolean $object_mode Is the activity item is used inside another object? @@ -547,13 +578,18 @@ class Transmitter $data['published'] = DateTimeFormat::utc($item['created'] . '+00:00', DateTimeFormat::ATOM); + $data['instrument'] = ['type' => 'Service', 'name' => BaseObject::getApp()->getUserAgent()]; + $data = array_merge($data, self::createPermissionBlockForItem($item)); - if (in_array($data['type'], ['Create', 'Update', 'Announce', 'Delete'])) { + if (in_array($data['type'], ['Create', 'Update', 'Delete'])) { $data['object'] = self::createNote($item); + } elseif ($data['type'] == 'Announce') { + $data['object'] = self::createAnnounce($item); } elseif ($data['type'] == 'Undo') { $data['object'] = self::createActivityFromItem($item_id, true); } else { + $data['diaspora:guid'] = $item['guid']; $data['object'] = $item['thr-parent']; } @@ -569,7 +605,7 @@ class Transmitter } /** - * @brief Creates an object array for a given item id + * Creates an object array for a given item id * * @param integer $item_id * @@ -590,7 +626,41 @@ class Transmitter } /** - * @brief Returns a tag array for a given item array + * Creates a location entry for a given item array + * + * @param array $item + * + * @return array with location array + */ + private static function createLocation($item) + { + $location = ['type' => 'Place']; + + if (!empty($item['location'])) { + $location['name'] = $item['location']; + } + + $coord = []; + + if (empty($item['coord'])) { + $coord = Map::getCoordinates($item['location']); + } else { + $coords = explode(' ', $item['coord']); + if (count($coords) == 2) { + $coord = ['lat' => $coords[0], 'lon' => $coords[1]]; + } + } + + if (!empty($coord['lat']) && !empty($coord['lon'])) { + $location['latitude'] = $coord['lat']; + $location['longitude'] = $coord['lon']; + } + + return $location; + } + + /** + * Returns a tag array for a given item array * * @param array $item * @@ -619,7 +689,7 @@ class Transmitter } /** - * @brief Adds attachment data to the JSON document + * Adds attachment data to the JSON document * * @param array $item Data of the item that is to be posted * @param text $type Object type @@ -674,7 +744,7 @@ class Transmitter } /** - * @brief Remove image elements and replaces them with links to the image + * Remove image elements and replaces them with links to the image * * @param string $body * @@ -692,7 +762,7 @@ class Transmitter } /** - * @brief Fetches the "context" value for a givem item array from the "conversation" table + * Fetches the "context" value for a givem item array from the "conversation" table * * @param array $item * @@ -712,7 +782,7 @@ class Transmitter } /** - * @brief Returns if the post contains sensitive content ("nsfw") + * Returns if the post contains sensitive content ("nsfw") * * @param integer $item_id * @@ -725,7 +795,7 @@ class Transmitter } /** - * @brief Creates a note/article object array + * Creates a note/article object array * * @param array $item * @@ -768,12 +838,11 @@ class Transmitter $data['url'] = $item['plink']; $data['attributedTo'] = $item['author-link']; - $data['actor'] = $item['author-link']; $data['sensitive'] = self::isSensitive($item['id']); $data['context'] = self::fetchContextURLForItem($item); if (!empty($item['title'])) { - $data['name'] = BBCode::convert($item['title'], false, 7); + $data['name'] = BBCode::toPlaintext($item['title'], false); } $body = $item['body']; @@ -791,13 +860,69 @@ class Transmitter $data['attachment'] = self::createAttachmentList($item, $type); $data['tag'] = self::createTagList($item); + + if (!empty($item['coord']) || !empty($item['location'])) { + $data['location'] = self::createLocation($item); + } + + if (!empty($item['app'])) { + $data['generator'] = ['type' => 'Application', 'name' => $item['app']]; + } + $data = array_merge($data, self::createPermissionBlockForItem($item)); return $data; } /** - * @brief Transmits a profile deletion to a given inbox + * Creates an announce object entry + * + * @param array $item + * + * @return string with announced object url + */ + public static function createAnnounce($item) + { + $announce = api_share_as_retweet($item); + if (empty($announce['plink'])) { + return self::createNote($item); + } + + return $announce['plink']; + } + + /** + * Transmits a contact suggestion to a given inbox + * + * @param integer $uid User ID + * @param string $inbox Target inbox + * @param integer $suggestion_id Suggestion ID + */ + public static function sendContactSuggestion($uid, $inbox, $suggestion_id) + { + $owner = User::getOwnerDataById($uid); + $profile = APContact::getByURL($owner['url']); + + $suggestion = DBA::selectFirst('fsuggest', ['url', 'note', 'created'], ['id' => $suggestion_id]); + + $data = ['@context' => 'https://www.w3.org/ns/activitystreams', + 'id' => System::baseUrl() . '/activity/' . System::createGUID(), + 'type' => 'Announce', + 'actor' => $owner['url'], + 'object' => $suggestion['url'], + 'content' => $suggestion['note'], + 'instrument' => ['type' => 'Service', 'name' => BaseObject::getApp()->getUserAgent()], + 'to' => [ActivityPub::PUBLIC_COLLECTION], + 'cc' => []]; + + $signed = LDSignature::sign($data, $owner); + + logger('Deliver profile deletion for user ' . $uid . ' to ' . $inbox . ' via ActivityPub', LOGGER_DEBUG); + HTTPSignature::transmit($signed, $inbox, $uid); + } + + /** + * Transmits a profile deletion to a given inbox * * @param integer $uid User ID * @param string $inbox Target inbox @@ -813,6 +938,7 @@ class Transmitter 'actor' => $owner['url'], 'object' => $owner['url'], 'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM), + 'instrument' => ['type' => 'Service', 'name' => BaseObject::getApp()->getUserAgent()], 'to' => [ActivityPub::PUBLIC_COLLECTION], 'cc' => []]; @@ -823,7 +949,7 @@ class Transmitter } /** - * @brief Transmits a profile change to a given inbox + * Transmits a profile change to a given inbox * * @param integer $uid User ID * @param string $inbox Target inbox @@ -839,6 +965,7 @@ class Transmitter 'actor' => $owner['url'], 'object' => self::getProfile($uid), 'published' => DateTimeFormat::utcNow(DateTimeFormat::ATOM), + 'instrument' => ['type' => 'Service', 'name' => BaseObject::getApp()->getUserAgent()], 'to' => [$profile['followers']], 'cc' => []]; @@ -849,7 +976,7 @@ class Transmitter } /** - * @brief Transmits a given activity to a target + * Transmits a given activity to a target * * @param array $activity * @param string $target Target profile @@ -866,6 +993,7 @@ class Transmitter 'type' => $activity, 'actor' => $owner['url'], 'object' => $profile['url'], + 'instrument' => ['type' => 'Service', 'name' => BaseObject::getApp()->getUserAgent()], 'to' => $profile['url']]; logger('Sending activity ' . $activity . ' to ' . $target . ' for user ' . $uid, LOGGER_DEBUG); @@ -875,7 +1003,7 @@ class Transmitter } /** - * @brief Transmit a message that the contact request had been accepted + * Transmit a message that the contact request had been accepted * * @param string $target Target profile * @param $id @@ -893,6 +1021,7 @@ class Transmitter 'object' => ['id' => $id, 'type' => 'Follow', 'actor' => $profile['url'], 'object' => $owner['url']], + 'instrument' => ['type' => 'Service', 'name' => BaseObject::getApp()->getUserAgent()], 'to' => $profile['url']]; logger('Sending accept to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG); @@ -902,7 +1031,7 @@ class Transmitter } /** - * @brief Reject a contact request or terminates the contact relation + * Reject a contact request or terminates the contact relation * * @param string $target Target profile * @param $id @@ -920,6 +1049,7 @@ class Transmitter 'object' => ['id' => $id, 'type' => 'Follow', 'actor' => $profile['url'], 'object' => $owner['url']], + 'instrument' => ['type' => 'Service', 'name' => BaseObject::getApp()->getUserAgent()], 'to' => $profile['url']]; logger('Sending reject to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG); @@ -929,7 +1059,7 @@ class Transmitter } /** - * @brief Transmits a message that we don't want to follow this contact anymore + * Transmits a message that we don't want to follow this contact anymore * * @param string $target Target profile * @param integer $uid User ID @@ -948,6 +1078,7 @@ class Transmitter 'object' => ['id' => $id, 'type' => 'Follow', 'actor' => $owner['url'], 'object' => $profile['url']], + 'instrument' => ['type' => 'Service', 'name' => BaseObject::getApp()->getUserAgent()], 'to' => $profile['url']]; logger('Sending undo to ' . $target . ' for user ' . $uid . ' with id ' . $id, LOGGER_DEBUG);