if (!DBA::isResult($item)) {
Logger::warning('No existing item, item will be created', ['uri' => $activity['id']]);
$item = self::createItem($activity);
+ if (empty($item)) {
+ return;
+ }
+
self::postItem($activity, $item);
return;
}
/**
* Update an existing event
*
- * @param int $event_id
- * @param array $activity
+ * @param int $event_id
+ * @param array $activity
*/
private static function updateEvent(int $event_id, array $activity)
{
if (empty($activity['directmessage']) && ($activity['id'] != $activity['reply-to-id']) && !Post::exists(['uri' => $activity['reply-to-id']])) {
Logger::notice('Parent not found. Try to refetch it.', ['parent' => $activity['reply-to-id']]);
- self::fetchMissingActivity($activity['reply-to-id'], $activity);
+ self::fetchMissingActivity($activity['reply-to-id'], $activity, '', Receiver::COMPLETION_AUTO);
}
$item['diaspora_signed_text'] = $activity['diaspora:comment'] ?? '';
public static function createActivity($activity, $verb)
{
$item = self::createItem($activity);
+ if (empty($item)) {
+ return;
+ }
+
$item['verb'] = $verb;
$item['thr-parent'] = $activity['object_id'];
$item['gravity'] = GRAVITY_ACTIVITY;
unset($item['post-type']);
$item['object-type'] = Activity\ObjectType::NOTE;
+ if (!empty($activity['content'])) {
+ $item['body'] = HTML::toBBCode($activity['content']);
+ }
+
$item['diaspora_signed_text'] = $activity['diaspora:like'] ?? '';
self::postItem($activity, $item);
}
+ /**
+ * Fetch the Uri-Id of a post for the "featured" collection
+ *
+ * @param array $activity
+ * @return null|int
+ */
+ private static function getUriIdForFeaturedCollection(array $activity)
+ {
+ $actor = APContact::getByURL($activity['actor']);
+ if (empty($actor)) {
+ return null;
+ }
+
+ // Refetch the account when the "featured" collection is missing.
+ // This can be removed in a future version (end of 2022 should be good).
+ if (empty($actor['featured'])) {
+ $actor = APContact::getByURL($activity['actor'], true);
+ if (empty($actor)) {
+ return null;
+ }
+ }
+
+ if ($activity['target_id'] != $actor['featured']) {
+ return null;
+ }
+
+ $id = Contact::getIdForURL($activity['actor']);
+ if (empty($id)) {
+ return null;
+ }
+
+ $parent = Post::selectFirst(['uri-id'], ['uri' => $activity['object_id'], 'author-id' => $id]);
+ if (!empty($parent['uri-id'])) {
+ return $parent['uri-id'];
+ }
+
+ return null;
+ }
+
+ /**
+ * Add a post to the "Featured" collection
+ *
+ * @param array $activity
+ */
+ public static function addToFeaturedCollection(array $activity)
+ {
+ $uriid = self::getUriIdForFeaturedCollection($activity);
+ if (empty($uriid)) {
+ return;
+ }
+
+ Logger::debug('Add post to featured collection', ['uri-id' => $uriid]);
+
+ Post\Collection::add($uriid, Post\Collection::FEATURED);
+ }
+
+ /**
+ * Remove a post to the "Featured" collection
+ *
+ * @param array $activity
+ */
+ public static function removeFromFeaturedCollection(array $activity)
+ {
+ $uriid = self::getUriIdForFeaturedCollection($activity);
+ if (empty($uriid)) {
+ return;
+ }
+
+ Logger::debug('Remove post from featured collection', ['uri-id' => $uriid]);
+
+ Post\Collection::remove($uriid, Post\Collection::FEATURED);
+ }
+
/**
* Create an event
*
*/
public static function createEvent($activity, $item)
{
- $event['summary'] = HTML::toBBCode($activity['name']);
+ $event['summary'] = HTML::toBBCode($activity['name'] ?: $activity['summary']);
$event['desc'] = HTML::toBBCode($activity['content']);
$event['start'] = $activity['start-time'];
$event['finish'] = $activity['end-time'];
}
/**
- * Generate a GUID out of an URL
+ * Generate a GUID out of an URL of an ActivityPub post.
*
* @param string $url message URL
* @return string with GUID
Logger::debug('Message is private - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
return true;
}
-
+
if (!empty($activity['from-relay'])) {
// We check relay posts at another place. When it arrived here, the message is already checked.
Logger::debug('Message is a relay post that is already checked - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
return true;
}
- if (!empty($activity['thread-completion'])) {
- // The thread completion mode means that the post is fetched intentionally.
- // This can have several causes, in doubt we keep the message.
- // This can possibly be improved in the future.
- Logger::debug('Message is in completion mode - accepted', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
+ if (in_array($activity['completion-mode'] ?? Receiver::COMPLETION_NONE, [Receiver::COMPLETION_MANUAL, Receiver::COMPLETION_ANNOUCE])) {
+ // Manual completions and completions caused by reshares are allowed without any further checks.
+ Logger::debug('Message is in completion mode - accepted', ['mode' => $activity['completion-mode'], 'uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
return true;
}
}
$tags = array_column(Tag::getByURIId($item['uri-id'], [Tag::HASHTAG]), 'name');
- return Relay::isSolicitedPost($tags, $item['body'], $item['author-id'], $item['uri'], Protocol::ACTIVITYPUB);
+ if (Relay::isSolicitedPost($tags, $item['body'], $item['author-id'], $item['uri'], Protocol::ACTIVITYPUB)) {
+ Logger::debug('Post is accepted because of the relay settings', ['uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
+ return true;
+ } else {
+ return false;
+ }
}
/**
return Mail::insert($msg);
}
+ /**
+ * Fetch featured posts from a contact with the given url
+ *
+ * @param string $url
+ * @return void
+ */
+ public static function fetchFeaturedPosts(string $url)
+ {
+ Logger::info('Fetch featured posts', ['contact' => $url]);
+
+ $apcontact = APContact::getByURL($url);
+ if (empty($apcontact['featured'])) {
+ Logger::info('Contact does not have a featured collection', ['contact' => $url]);
+ return;
+ }
+
+ $pcid = Contact::getIdForURL($url, 0, false);
+ if (empty($pcid)) {
+ Logger::info('Contact not found', ['contact' => $url]);
+ return;
+ }
+
+ $posts = Post\Collection::selectToArrayForContact($pcid, Post\Collection::FEATURED);
+ if (!empty($posts)) {
+ $old_featured = array_column($posts, 'uri-id');
+ } else {
+ $old_featured = [];
+ }
+
+ $featured = ActivityPub::fetchItems($apcontact['featured']);
+ if (empty($featured)) {
+ Logger::info('Contact does not have featured posts', ['contact' => $url]);
+
+ foreach ($old_featured as $uri_id) {
+ Post\Collection::remove($uri_id, Post\Collection::FEATURED);
+ Logger::debug('Removed no longer featured post', ['uri-id' => $uri_id, 'contact' => $url]);
+ }
+ return;
+ }
+
+ $new = 0;
+ $old = 0;
+
+ foreach ($featured as $post) {
+ if (empty($post['id'])) {
+ continue;
+ }
+ $id = Item::fetchByLink($post['id']);
+ if (!empty($id)) {
+ $item = Post::selectFirst(['uri-id', 'featured'], ['id' => $id]);
+ if (!empty($item['uri-id'])) {
+ if (!$item['featured']) {
+ Post\Collection::add($item['uri-id'], Post\Collection::FEATURED);
+ Logger::debug('Added featured post', ['uri-id' => $item['uri-id'], 'contact' => $url]);
+ $new++;
+ } else {
+ Logger::debug('Post already had been featured', ['uri-id' => $item['uri-id'], 'contact' => $url]);
+ $old++;
+ }
+
+ $index = array_search($item['uri-id'], $old_featured);
+ if (!($index === false)) {
+ unset($old_featured[$index]);
+ }
+ }
+ }
+ }
+
+ foreach ($old_featured as $uri_id) {
+ Post\Collection::remove($uri_id, Post\Collection::FEATURED);
+ Logger::debug('Removed no longer featured post', ['uri-id' => $uri_id, 'contact' => $url]);
+ }
+
+ Logger::info('Fetched featured posts', ['new' => $new, 'old' => $old, 'contact' => $url]);
+ }
+
/**
* Fetches missing posts
*
* @param string $url message URL
* @param array $child activity array with the child of this message
* @param string $relay_actor Relay actor
+ * @param int $completion Completion mode, see Receiver::COMPLETION_*
* @return string fetched message URL
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
- public static function fetchMissingActivity(string $url, array $child = [], string $relay_actor = '')
+ public static function fetchMissingActivity(string $url, array $child = [], string $relay_actor = '', int $completion = Receiver::COMPLETION_MANUAL)
{
if (!empty($child['receiver'])) {
$uid = ActivityPub\Receiver::getFirstUserFromReceivers($child['receiver']);
if (!empty($relay_actor)) {
$ldactivity['thread-completion'] = $ldactivity['from-relay'] = Contact::getIdForURL($relay_actor);
+ $ldactivity['completion-mode'] = Receiver::COMPLETION_RELAY;
} elseif (!empty($child['thread-completion'])) {
$ldactivity['thread-completion'] = $child['thread-completion'];
+ $ldactivity['completion-mode'] = $child['completion-mode'] ?? Receiver::COMPLETION_NONE;
} else {
$ldactivity['thread-completion'] = Contact::getIdForURL($actor);
+ $ldactivity['completion-mode'] = $completion;
}
if (!empty($child['type'])) {
Logger::info('Deleted contact', ['object' => $activity['object_id']]);
}
+ /**
+ * Blocks the user by the contact
+ *
+ * @param array $activity
+ * @throws \Exception
+ */
+ public static function blockAccount($activity)
+ {
+ $cid = Contact::getIdForURL($activity['actor']);
+ if (empty($cid)) {
+ return;
+ }
+
+ $uid = User::getIdForURL($activity['object_id']);
+ if (empty($uid)) {
+ return;
+ }
+
+ Contact\User::setIsBlocked($cid, $uid, true);
+
+ Logger::info('Contact blocked user', ['contact' => $cid, 'user' => $uid]);
+ }
+
+ /**
+ * Unblocks the user by the contact
+ *
+ * @param array $activity
+ * @throws \Exception
+ */
+ public static function unblockAccount($activity)
+ {
+ $cid = Contact::getIdForURL($activity['actor']);
+ if (empty($cid)) {
+ return;
+ }
+
+ $uid = User::getIdForURL($activity['object_object']);
+ if (empty($uid)) {
+ return;
+ }
+
+ Contact\User::setIsBlocked($cid, $uid, false);
+
+ Logger::info('Contact unblocked user', ['contact' => $cid, 'user' => $uid]);
+ }
+
/**
* Accept a follow request
*