X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FProtocol%2FActivityPub%2FTransmitter.php;h=545ac22c699b0a668cfe486c7d17f617dd01d123;hb=85b3c880ef1ba48091acdfe649c35eef6c1c8f15;hp=06dbaee71f81f1c619a9e857dde8dd80c465cb01;hpb=ea218438d2913e128c11728158ef80b43c826e89;p=friendica.git diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 06dbaee71f..545ac22c69 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -6,30 +6,32 @@ namespace Friendica\Protocol\ActivityPub; use Friendica\BaseObject; use Friendica\Content\Feature; -use Friendica\Database\DBA; +use Friendica\Content\Text\BBCode; +use Friendica\Content\Text\Plaintext; +use Friendica\Core\Cache; use Friendica\Core\Config; use Friendica\Core\Logger; -use Friendica\Core\System; -use Friendica\Util\HTTPSignature; use Friendica\Core\Protocol; -use Friendica\Model\Conversation; -use Friendica\Model\Contact; +use Friendica\Core\System; +use Friendica\Database\DBA; use Friendica\Model\APContact; +use Friendica\Model\Contact; +use Friendica\Model\Conversation; use Friendica\Model\Item; +use Friendica\Model\Profile; +use Friendica\Model\Photo; use Friendica\Model\Term; use Friendica\Model\User; +use Friendica\Protocol\Activity; +use Friendica\Protocol\ActivityPub; use Friendica\Util\DateTimeFormat; -use Friendica\Content\Text\BBCode; -use Friendica\Content\Text\Plaintext; -use Friendica\Util\XML; +use Friendica\Util\HTTPSignature; +use Friendica\Util\Images; use Friendica\Util\JsonLD; use Friendica\Util\LDSignature; -use Friendica\Model\Profile; -use Friendica\Object\Image; -use Friendica\Protocol\ActivityPub; -use Friendica\Core\Cache; use Friendica\Util\Map; use Friendica\Util\Network; +use Friendica\Util\XML; require_once 'include/api.php'; require_once 'mod/share.php'; @@ -71,6 +73,7 @@ class Transmitter if (empty($page)) { $data['first'] = System::baseUrl() . '/followers/' . $owner['nickname'] . '?page=1'; } else { + $data['type'] = 'OrderedCollectionPage'; $list = []; $contacts = DBA::select('contact', ['url'], $condition, ['limit' => [($page - 1) * 100, 100]]); @@ -119,6 +122,7 @@ class Transmitter if (empty($page)) { $data['first'] = System::baseUrl() . '/following/' . $owner['nickname'] . '?page=1'; } else { + $data['type'] = 'OrderedCollectionPage'; $list = []; $contacts = DBA::select('contact', ['url'], $condition, ['limit' => [($page - 1) * 100, 100]]); @@ -165,6 +169,7 @@ class Transmitter if (empty($page)) { $data['first'] = System::baseUrl() . '/outbox/' . $owner['nickname'] . '?page=1'; } else { + $data['type'] = 'OrderedCollectionPage'; $list = []; $condition['parent-network'] = Protocol::NATIVE_SUPPORT; @@ -229,11 +234,6 @@ class Transmitter 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']; @@ -254,7 +254,7 @@ class Transmitter 'publicKeyPem' => $user['pubkey']]; $data['endpoints'] = ['sharedInbox' => System::baseUrl() . '/inbox']; $data['icon'] = ['type' => 'Image', - 'url' => $contact['avatar']]; + 'url' => $contact['photo']]; $data['generator'] = self::getService(); @@ -363,7 +363,7 @@ class Transmitter if (Config::get('debug', 'total_ap_delivery')) { // Will be activated in a later step - $networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS]; + $networks = Protocol::FEDERATED; } else { // For now only send to these contacts: $networks = [Protocol::ACTIVITYPUB, Protocol::OSTATUS]; @@ -391,7 +391,7 @@ class Transmitter } } } else { - $receiver_list = Item::enumeratePermissions($item); + $receiver_list = Item::enumeratePermissions($item, true); foreach ($terms as $term) { $cid = Contact::getIdForURL($term['url'], $item['uid']); @@ -436,7 +436,7 @@ class Transmitter $data['to'][] = $profile['url']; } else { $data['cc'][] = $profile['url']; - if (!$item['private']) { + if (!$item['private'] && !empty($actor_profile['followers'])) { $data['cc'][] = $actor_profile['followers']; } } @@ -535,7 +535,7 @@ class Transmitter if (Config::get('debug', 'total_ap_delivery')) { // Will be activated in a later step - $networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS]; + $networks = Protocol::FEDERATED; } else { // For now only send to these contacts: $networks = [Protocol::ACTIVITYPUB, Protocol::OSTATUS]; @@ -549,6 +549,10 @@ class Transmitter $contacts = DBA::select('contact', ['url', 'network', 'protocol'], $condition); while ($contact = DBA::fetch($contacts)) { + if (Contact::isLocal($contact['url'])) { + continue; + } + if (!in_array($contact['network'], $networks) && ($contact['protocol'] != Protocol::ACTIVITYPUB)) { continue; } @@ -616,6 +620,10 @@ class Transmitter if ($receiver == $item_profile['followers']) { $inboxes = array_merge($inboxes, self::fetchTargetInboxesforUser($uid, $personal)); } else { + if (Contact::isLocal($receiver)) { + continue; + } + $profile = APContact::getByURL($receiver, false); if (!empty($profile)) { if (empty($profile['sharedinbox']) || $personal || $blindcopy) { @@ -755,25 +763,25 @@ class Transmitter if ($reshared) { $type = 'Announce'; - } elseif ($item['verb'] == ACTIVITY_POST) { + } elseif ($item['verb'] == Activity::POST) { if ($item['created'] == $item['edited']) { $type = 'Create'; } else { $type = 'Update'; } - } elseif ($item['verb'] == ACTIVITY_LIKE) { + } elseif ($item['verb'] == Activity::LIKE) { $type = 'Like'; - } elseif ($item['verb'] == ACTIVITY_DISLIKE) { + } elseif ($item['verb'] == Activity::DISLIKE) { $type = 'Dislike'; - } elseif ($item['verb'] == ACTIVITY_ATTEND) { + } elseif ($item['verb'] == Activity::ATTEND) { $type = 'Accept'; - } elseif ($item['verb'] == ACTIVITY_ATTENDNO) { + } elseif ($item['verb'] == Activity::ATTENDNO) { $type = 'Reject'; - } elseif ($item['verb'] == ACTIVITY_ATTENDMAYBE) { + } elseif ($item['verb'] == Activity::ATTENDMAYBE) { $type = 'TentativeAccept'; - } elseif ($item['verb'] == ACTIVITY_FOLLOW) { + } elseif ($item['verb'] == Activity::FOLLOW) { $type = 'Follow'; - } elseif ($item['verb'] == ACTIVITY_TAG) { + } elseif ($item['verb'] == Activity::TAG) { $type = 'Add'; } else { $type = ''; @@ -1039,10 +1047,10 @@ class Transmitter // Simplify image codes $body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $item['body']); - // Grab all pictures and create attachments out of them + // Grab all pictures without alternative descriptions and create attachments out of them if (preg_match_all("/\[img\]([^\[\]]*)\[\/img\]/Usi", $body, $pictures)) { foreach ($pictures[1] as $picture) { - $imgdata = Image::getInfoFromURL($picture); + $imgdata = Images::getInfoFromURLCached($picture); if ($imgdata) { $attachments[] = ['type' => 'Document', 'mediaType' => $imgdata['mime'], @@ -1052,6 +1060,19 @@ class Transmitter } } + // Grab all pictures with alternative description and create attachments out of them + if (preg_match_all("/\[img=([^\[\]]*)\]([^\[\]]*)\[\/img\]/Usi", $body, $pictures, PREG_SET_ORDER)) { + foreach ($pictures as $picture) { + $imgdata = Images::getInfoFromURLCached($picture[1]); + if ($imgdata) { + $attachments[] = ['type' => 'Document', + 'mediaType' => $imgdata['mime'], + 'url' => $picture[1], + 'name' => $picture[2]]; + } + } + } + return $attachments; } @@ -1077,19 +1098,34 @@ class Transmitter } /** - * Remove image elements and replaces them with links to the image + * Remove image elements since they are added as attachment * * @param string $body * - * @return string with replaced elements + * @return string with removed images */ private static function removePictures($body) { // Simplify image codes $body = preg_replace("/\[img\=([0-9]*)x([0-9]*)\](.*?)\[\/img\]/ism", '[img]$3[/img]', $body); + $body = preg_replace("/\[img\=(.*?)\](.*?)\[\/img\]/ism", '[img]$1[/img]', $body); + + // Now remove local links + $body = preg_replace_callback( + '/\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]/Usi', + function ($match) { + // We remove the link when it is a link to a local photo page + if (Photo::isLocalPage($match[1])) { + return ''; + } + // otherwise we just return the link + return '[url]' . $match[1] . '[/url]'; + }, + $body + ); - $body = preg_replace("/\[url=([^\[\]]*)\]\[img\](.*)\[\/img\]\[\/url\]/Usi", '[url]$1[/url]', $body); - $body = preg_replace("/\[img\]([^\[\]]*)\[\/img\]/Usi", '[url]$1[/url]', $body); + // Remove all pictures + $body = preg_replace("/\[img\]([^\[\]]*)\[\/img\]/Usi", '', $body); return $body; } @@ -1141,7 +1177,7 @@ class Transmitter { $event = []; $event['name'] = $item['event-summary']; - $event['content'] = BBCode::convert($item['event-desc'], false, 7); + $event['content'] = BBCode::convert($item['event-desc'], false, 9); $event['startTime'] = DateTimeFormat::utc($item['event-start'] . '+00:00', DateTimeFormat::ATOM); if (!$item['event-nofinish']) { @@ -1231,9 +1267,15 @@ class Transmitter $regexp = "/[@!]\[url\=([^\[\]]*)\].*?\[\/url\]/ism"; $body = preg_replace_callback($regexp, ['self', 'mentionCallback'], $body); - $data['content'] = BBCode::convert($body, false, 7); + $data['content'] = BBCode::convert($body, false, 9); } + $regexp = "/[@!]\[url\=([^\[\]]*)\].*?\[\/url\]/ism"; + $richbody = preg_replace_callback($regexp, ['self', 'mentionCallback'], $item['body']); + + $data['contentMap']['text/html'] = BBCode::convert($richbody, false); + $data['contentMap']['text/markdown'] = BBCode::toMarkdown($item["body"]); + $data['source'] = ['content' => $item['body'], 'mediaType' => "text/bbcode"]; if (!empty($item['signed_text']) && ($item['uri'] != $item['thr-parent'])) { @@ -1292,6 +1334,7 @@ class Transmitter */ private static function createAnnounce($item, $data) { + $orig_body = $item['body']; $announce = api_share_as_retweet($item); if (empty($announce['plink'])) { $data['type'] = 'Create'; @@ -1304,12 +1347,29 @@ class Transmitter if (!empty($activity)) { $ldactivity = JsonLD::compact($activity); $id = JsonLD::fetchElement($ldactivity, '@id'); + $type = str_replace('as:', '', JsonLD::fetchElement($ldactivity, '@type')); if (!empty($id)) { - $data['object'] = $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; } } + $item['body'] = $orig_body; $data['type'] = 'Create'; $data['object'] = self::createNote($item); return $data; @@ -1479,6 +1539,10 @@ class Transmitter public static function sendActivity($activity, $target, $uid, $id = '') { $profile = APContact::getByURL($target); + if (empty($profile['inbox'])) { + Logger::warning('No inbox found for target', ['target' => $target, 'profile' => $profile]); + return; + } $owner = User::getOwnerDataById($uid); @@ -1515,6 +1579,10 @@ class Transmitter public static function sendFollowObject($object, $target, $uid = 0) { $profile = APContact::getByURL($target); + if (empty($profile['inbox'])) { + Logger::warning('No inbox found for target', ['target' => $target, 'profile' => $profile]); + return; + } if (empty($uid)) { // Fetch the list of administrators @@ -1526,7 +1594,7 @@ class Transmitter $uid = $first_user['uid']; } - $condition = ['verb' => ACTIVITY_FOLLOW, 'uid' => 0, 'parent-uri' => $object, + $condition = ['verb' => Activity::FOLLOW, 'uid' => 0, 'parent-uri' => $object, 'author-id' => Contact::getPublicIdByUserId($uid)]; if (Item::exists($condition)) { Logger::log('Follow for ' . $object . ' for user ' . $uid . ' does already exist.', Logger::DEBUG); @@ -1561,6 +1629,10 @@ class Transmitter public static function sendContactAccept($target, $id, $uid) { $profile = APContact::getByURL($target); + if (empty($profile['inbox'])) { + Logger::warning('No inbox found for target', ['target' => $target, 'profile' => $profile]); + return; + } $owner = User::getOwnerDataById($uid); $data = ['@context' => ActivityPub::CONTEXT, @@ -1594,6 +1666,10 @@ class Transmitter public static function sendContactReject($target, $id, $uid) { $profile = APContact::getByURL($target); + if (empty($profile['inbox'])) { + Logger::warning('No inbox found for target', ['target' => $target, 'profile' => $profile]); + return; + } $owner = User::getOwnerDataById($uid); $data = ['@context' => ActivityPub::CONTEXT, @@ -1627,6 +1703,10 @@ class Transmitter public static function sendContactUndo($target, $cid, $uid) { $profile = APContact::getByURL($target); + if (empty($profile['inbox'])) { + Logger::warning('No inbox found for target', ['target' => $target, 'profile' => $profile]); + return; + } $object_id = self::activityIDFromContact($cid); if (empty($object_id)) {