diff --git a/src/BaseModule.php b/src/BaseModule.php index 27f156bef0..933f38600b 100644 --- a/src/BaseModule.php +++ b/src/BaseModule.php @@ -512,7 +512,7 @@ abstract class BaseModule implements ICanHandleRequests * @throws HTTPException\InternalServerErrorException * @see json_encode() */ - public function jsonExit($content, string $content_type = 'application/json', int $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) + public function jsonExit($content, string $content_type = 'application/json; charset=utf-8', int $options = JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) { $this->httpExit(json_encode($content, $options), ICanCreateResponses::TYPE_JSON, $content_type); } diff --git a/src/Factory/Api/Mastodon/Relationship.php b/src/Factory/Api/Mastodon/Relationship.php index d12b563f10..7411f063f9 100644 --- a/src/Factory/Api/Mastodon/Relationship.php +++ b/src/Factory/Api/Mastodon/Relationship.php @@ -38,7 +38,8 @@ class Relationship extends BaseFactory $pcid, $contact, Contact\User::isBlocked($cid, $uid), - Contact\User::isIgnored($cid, $uid) + Contact\User::isIgnored($cid, $uid), + Contact\User::isIsBlocked($cid, $uid), ); } } diff --git a/src/Module/Api/Mastodon/Accounts/Relationships.php b/src/Module/Api/Mastodon/Accounts/Relationships.php index 4f0c79ebfc..afdcf4cdeb 100644 --- a/src/Module/Api/Mastodon/Accounts/Relationships.php +++ b/src/Module/Api/Mastodon/Accounts/Relationships.php @@ -28,7 +28,7 @@ class Relationships extends BaseApi ], $request); if (empty($request['id'])) { - $this->logAndJsonError(422, $this->errorFactory->UnprocessableEntity()); + $this->jsonExit([]); } if (!is_array($request['id'])) { diff --git a/src/Module/Api/Mastodon/Search.php b/src/Module/Api/Mastodon/Search.php index 9301c60cdc..668bb4d8d0 100644 --- a/src/Module/Api/Mastodon/Search.php +++ b/src/Module/Api/Mastodon/Search.php @@ -50,6 +50,10 @@ class Search extends BaseApi $limit = min($request['limit'], 40); + if (Network::isValidHttpUrl($request['q']) && ($request['offset'] == 0)) { + $this->searchLinks($uid, $request['q'], $request['type']); + } + $result = ['accounts' => [], 'statuses' => [], 'hashtags' => []]; if (empty($request['type']) || ($request['type'] == 'accounts')) { @@ -79,6 +83,43 @@ class Search extends BaseApi $this->jsonExit($result); } + /** + * Search for links (either accounts or statuses). Return an empty result otherwise + * + * @param integer $uid User id + * @param string $q Search term (HTTP link) + * @param string $type Search type (or empty if not provided) + */ + private function searchLinks(int $uid, string $q, string $type) + { + $result = ['accounts' => [], 'statuses' => [], 'hashtags' => []]; + + $data = ['uri-id' => -1, 'type' => Post\Media::UNKNOWN, 'url' => $q]; + $data = Post\Media::fetchAdditionalData($data); + + if ((empty($type) || ($type == 'statuses')) && in_array($data['type'], [Post\Media::HTML, Post\Media::ACTIVITY, Post\Media::UNKNOWN])) { + $q = Network::convertToIdn($q); + // If the user-specific search failed, we search and probe a public post + $item_id = Item::fetchByLink($q, $uid) ?: Item::fetchByLink($q); + if ($item_id && $item = Post::selectFirst(['uri-id'], ['id' => $item_id])) { + $result['statuses'] = [DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, self::appSupportsQuotes())]; + $this->jsonExit($result); + } + } + + if ((empty($type) || ($type == 'accounts')) && in_array($data['type'], [Post\Media::HTML, Post\Media::ACCOUNT, Post\Media::UNKNOWN])) { + $id = Contact::getIdForURL($q, 0, false); + if ($id) { + $result['accounts'] = [DI::mstdnAccount()->createFromContactId($id, $uid)]; + $this->jsonExit($result); + } + } + + if (in_array($data['type'], [Post\Media::HTML, Post\Media::TEXT, Post\Media::ACCOUNT, Post\Media::ACTIVITY, Post\Media::UNKNOWN])) { + $this->jsonExit($result); + } + } + /** * @param int $uid * @param string $q @@ -93,9 +134,7 @@ class Search extends BaseApi */ private function searchAccounts(int $uid, string $q, bool $resolve, int $limit, int $offset, bool $following) { - if (($offset == 0) && (strrpos($q, '@') > 0 || Network::isValidHttpUrl($q)) - && $id = Contact::getIdForURL($q, 0, $resolve ? null : false) - ) { + if (($offset == 0) && (strrpos($q, '@') > 0) && $id = Contact::getIdForURL($q, 0, $resolve ? null : false)) { return DI::mstdnAccount()->createFromContactId($id, $uid); } @@ -122,19 +161,6 @@ class Search extends BaseApi */ private function searchStatuses(int $uid, string $q, string $account_id, int $max_id, int $min_id, int $limit, int $offset) { - if (Network::isValidHttpUrl($q)) { - // Unique post search, any offset greater than 0 should return empty result - if ($offset > 0) { - return []; - } - $q = Network::convertToIdn($q); - // If the user-specific search failed, we search and probe a public post - $item_id = Item::fetchByLink($q, $uid) ?: Item::fetchByLink($q); - if ($item_id && $item = Post::selectFirst(['uri-id'], ['id' => $item_id])) { - return DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid, self::appSupportsQuotes()); - } - } - $params = ['order' => ['uri-id' => true], 'limit' => [$offset, $limit]]; if (substr($q, 0, 1) == '#') { diff --git a/src/Object/Api/Mastodon/Account.php b/src/Object/Api/Mastodon/Account.php index de6cac6041..864b263cca 100644 --- a/src/Object/Api/Mastodon/Account.php +++ b/src/Object/Api/Mastodon/Account.php @@ -38,6 +38,8 @@ class Account extends BaseDataTransferObject /** @var bool */ protected $discoverable; /** @var bool */ + protected $indexable; + /** @var bool */ protected $group; /** @var string|null (Datetime) */ protected $created_at; @@ -45,6 +47,8 @@ class Account extends BaseDataTransferObject protected $note; /** @var string (URL)*/ protected $url; + /** @var string (URL)*/ + protected $uri; /** @var string (URL) */ protected $avatar; /** @var string (URL) */ @@ -61,6 +65,8 @@ class Account extends BaseDataTransferObject protected $statuses_count; /** @var string|null (Datetime) */ protected $last_status_at = null; + /** @var bool */ + protected $hide_collections = false; /** @var Emoji[] */ protected $emojis; /** @var Account|null */ @@ -88,12 +94,14 @@ class Account extends BaseDataTransferObject $this->locked = (bool)$account['manually-approve']; $this->bot = ($account['contact-type'] == Contact::TYPE_NEWS); $this->discoverable = !$account['unsearchable']; + $this->indexable = $this->discoverable; $this->group = ($account['contact-type'] == Contact::TYPE_COMMUNITY); $this->created_at = DateTimeFormat::utc($account['created'] ?: DBA::NULL_DATETIME, DateTimeFormat::JSON); $this->note = BBCode::convertForUriId($account['uri-id'], $account['about'], BBCode::EXTERNAL); - $this->url = $account['url']; + $this->url = $account['alias'] ?: $account['url']; + $this->uri = $account['url']; $this->avatar = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? ''); $this->avatar_static = Contact::getAvatarUrlForId($account['id'] ?? 0 ?: $account['pid'], Proxy::SIZE_SMALL, $account['updated'], $account['guid'] ?? '', true); $this->header = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? ''); diff --git a/src/Object/Api/Mastodon/Application.php b/src/Object/Api/Mastodon/Application.php index 50837f90cb..8126bebe55 100644 --- a/src/Object/Api/Mastodon/Application.php +++ b/src/Object/Api/Mastodon/Application.php @@ -63,8 +63,8 @@ class Application extends BaseDataTransferObject unset($application['redirect_uri']); } - if (empty($application['website'])) { - unset($application['website']); + if (empty($application['vapid_key'])) { + unset($application['vapid_key']); } return $application; diff --git a/src/Object/Api/Mastodon/Attachment.php b/src/Object/Api/Mastodon/Attachment.php index e3d7600f16..604ef41fac 100644 --- a/src/Object/Api/Mastodon/Attachment.php +++ b/src/Object/Api/Mastodon/Attachment.php @@ -27,6 +27,8 @@ class Attachment extends BaseDataTransferObject /** @var string */ protected $remote_url; /** @var string */ + protected $preview_remote_url; + /** @var string */ protected $text_url; /** @var string */ protected $description; @@ -52,7 +54,6 @@ class Attachment extends BaseDataTransferObject $this->url = $url; $this->preview_url = $preview; $this->remote_url = $remote; - $this->text_url = $this->remote_url ?? $this->url; $this->description = $attachment['description']; $this->blurhash = $attachment['blurhash']; if ($type === 'image') { diff --git a/src/Object/Api/Mastodon/Card.php b/src/Object/Api/Mastodon/Card.php index 7f097abe22..9b9c58db1b 100644 --- a/src/Object/Api/Mastodon/Card.php +++ b/src/Object/Api/Mastodon/Card.php @@ -41,9 +41,15 @@ class Card extends BaseDataTransferObject /** @var string */ protected $image; /** @var string */ + protected $image_description = ''; + /** @var string */ protected $embed_url; /** @var string */ protected $blurhash; + /** @var string|null (Datetime) */ + protected $published_at; + /** @var array */ + protected $authors = []; /** @var array */ protected $history; @@ -83,6 +89,10 @@ class Card extends BaseDataTransferObject return []; } + if (empty($this->history)) { + unset($this->history); + } + return parent::toArray(); } } diff --git a/src/Object/Api/Mastodon/Relationship.php b/src/Object/Api/Mastodon/Relationship.php index 1d708425cd..1158aa5459 100644 --- a/src/Object/Api/Mastodon/Relationship.php +++ b/src/Object/Api/Mastodon/Relationship.php @@ -46,15 +46,19 @@ class Relationship extends BaseDataTransferObject protected $blocking = false; /** @var bool */ protected $domain_blocking = false; + /** @var bool */ + protected $blocked_by = false; + /** + * Unsupported + * @var array + */ + protected $languages = []; /** * Unsupported * @var bool */ - protected $blocked_by = false; - /** - * Unsupported - * @var string - */ + protected $requested_by = false; + /** @var string */ protected $note = ''; /** @@ -63,7 +67,7 @@ class Relationship extends BaseDataTransferObject * @param bool $blocked "true" if user is blocked * @param bool $muted "true" if user is muted */ - public function __construct(int $contactId, array $contactRecord, bool $blocked = false, bool $muted = false) + public function __construct(int $contactId, array $contactRecord, bool $blocked = false, bool $muted = false, bool $isBlocked = false) { $this->id = (string)$contactId; $this->following = false; @@ -86,6 +90,7 @@ class Relationship extends BaseDataTransferObject $this->muting = (bool)($contactRecord['readonly'] ?? false) || $muted; $this->notifying = (bool)$contactRecord['notify_new_posts'] ?? false; $this->blocking = (bool)($contactRecord['blocked'] ?? false) || $blocked; + $this->blocked_by = $isBlocked; $this->note = $contactRecord['info']; } diff --git a/src/Object/Api/Mastodon/Status.php b/src/Object/Api/Mastodon/Status.php index 0e487a989c..16eba0afcb 100644 --- a/src/Object/Api/Mastodon/Status.php +++ b/src/Object/Api/Mastodon/Status.php @@ -64,6 +64,8 @@ class Status extends BaseDataTransferObject protected $pinned = false; /** @var string */ protected $content; + /** @var array */ + protected $filtered = []; /** @var Status|null */ protected $reblog = null; /** @var Status|null - Akkoma extension, see issue https://github.com/friendica/friendica/issues/12603 */ diff --git a/src/Object/Api/Mastodon/Tag.php b/src/Object/Api/Mastodon/Tag.php index 3c14a5e616..808e4c3cea 100644 --- a/src/Object/Api/Mastodon/Tag.php +++ b/src/Object/Api/Mastodon/Tag.php @@ -42,4 +42,13 @@ class Tag extends BaseDataTransferObject $this->history = $history; $this->following = $following; } + + public function toArray(): array + { + if (empty($this->history)) { + unset($this->history); + } + + return parent::toArray(); + } }