Merge pull request #14835 from annando/api

Improved Mastodon API compatibility
This commit is contained in:
Hypolite Petovan 2025-03-04 23:00:06 -05:00 committed by GitHub
commit af17e33d4e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 186 additions and 81 deletions

View file

@ -1,6 +1,6 @@
-- ------------------------------------------ -- ------------------------------------------
-- Friendica 2025.02-dev (Interrupted Fern) -- Friendica 2025.02-dev (Interrupted Fern)
-- DB_UPDATE_VERSION 1579 -- DB_UPDATE_VERSION 1580
-- ------------------------------------------ -- ------------------------------------------
@ -1441,6 +1441,9 @@ CREATE TABLE IF NOT EXISTS `post-media` (
`publisher-url` varbinary(383) COMMENT 'URL of the publisher of the media', `publisher-url` varbinary(383) COMMENT 'URL of the publisher of the media',
`publisher-name` varchar(255) COMMENT 'Name of the publisher of the media', `publisher-name` varchar(255) COMMENT 'Name of the publisher of the media',
`publisher-image` varbinary(383) COMMENT 'Image of the publisher of the media', `publisher-image` varbinary(383) COMMENT 'Image of the publisher of the media',
`language` char(3) COMMENT 'Language information about this media in the ISO 639 format',
`published` datetime COMMENT 'Publification date of this media',
`modified` datetime COMMENT 'Modification date of this media',
PRIMARY KEY(`id`), PRIMARY KEY(`id`),
UNIQUE INDEX `uri-id-url` (`uri-id`,`url`(512)), UNIQUE INDEX `uri-id-url` (`uri-id`,`url`(512)),
INDEX `uri-id-id` (`uri-id`,`id`), INDEX `uri-id-id` (`uri-id`,`id`),

View file

@ -30,6 +30,9 @@ Fields
| publisher-url | URL of the publisher of the media | varbinary(383) | YES | | NULL | | | publisher-url | URL of the publisher of the media | varbinary(383) | YES | | NULL | |
| publisher-name | Name of the publisher of the media | varchar(255) | YES | | NULL | | | publisher-name | Name of the publisher of the media | varchar(255) | YES | | NULL | |
| publisher-image | Image of the publisher of the media | varbinary(383) | YES | | NULL | | | publisher-image | Image of the publisher of the media | varbinary(383) | YES | | NULL | |
| language | Language information about this media in the ISO 639 format | char(3) | YES | | NULL | |
| published | Publification date of this media | datetime | YES | | NULL | |
| modified | Modification date of this media | datetime | YES | | NULL | |
Indexes Indexes
------------ ------------

View file

@ -512,7 +512,7 @@ abstract class BaseModule implements ICanHandleRequests
* @throws HTTPException\InternalServerErrorException * @throws HTTPException\InternalServerErrorException
* @see json_encode() * @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); $this->httpExit(json_encode($content, $options), ICanCreateResponses::TYPE_JSON, $content_type);
} }

View file

@ -48,6 +48,7 @@ class Card extends BaseFactory
$data['url'] = $media[0]['url']; $data['url'] = $media[0]['url'];
$data['title'] = $media[0]['name']; $data['title'] = $media[0]['name'];
$data['description'] = $media[0]['description']; $data['description'] = $media[0]['description'];
$data['language'] = $media[0]['language'];
$data['type'] = 'link'; $data['type'] = 'link';
$data['author_name'] = $media[0]['author-name']; $data['author_name'] = $media[0]['author-name'];
$data['author_url'] = $media[0]['author-url']; $data['author_url'] = $media[0]['author-url'];
@ -57,6 +58,7 @@ class Card extends BaseFactory
$data['width'] = $media[0]['preview-width']; $data['width'] = $media[0]['preview-width'];
$data['height'] = $media[0]['preview-height']; $data['height'] = $media[0]['preview-height'];
$data['blurhash'] = $media[0]['blurhash']; $data['blurhash'] = $media[0]['blurhash'];
$data['published'] = $media[0]['published'];
return new \Friendica\Object\Api\Mastodon\Card($data, $history); return new \Friendica\Object\Api\Mastodon\Card($data, $history);
} }

View file

@ -38,7 +38,8 @@ class Relationship extends BaseFactory
$pcid, $pcid,
$contact, $contact,
Contact\User::isBlocked($cid, $uid), Contact\User::isBlocked($cid, $uid),
Contact\User::isIgnored($cid, $uid) Contact\User::isIgnored($cid, $uid),
Contact\User::isIsBlocked($cid, $uid),
); );
} }
} }

View file

@ -362,6 +362,13 @@ class Media
$media['publisher-name'] = $gserver['site_name'] ?? null; $media['publisher-name'] = $gserver['site_name'] ?? null;
$media['publisher-image'] = null; $media['publisher-image'] = null;
if (!empty($item['language'])) {
$media['language'] = array_key_first(json_decode($item['language'], true));
}
$media['published'] = $item['created'];
$media['modified'] = $item['changed'];
DI::logger()->debug('Activity detected', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'plink' => $item['plink'], 'uri' => $item['uri']]); DI::logger()->debug('Activity detected', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'plink' => $item['plink'], 'uri' => $item['uri']]);
return $media; return $media;
} }
@ -403,6 +410,9 @@ class Media
$media['publisher-url'] = $gserver['url'] ?? null; $media['publisher-url'] = $gserver['url'] ?? null;
$media['publisher-name'] = $gserver['site_name'] ?? null; $media['publisher-name'] = $gserver['site_name'] ?? null;
$media['publisher-image'] = null; $media['publisher-image'] = null;
$media['language'] = null;
$media['published'] = $contact['created'];
$media['modified'] = $contact['updated'];
DI::logger()->debug('Account detected', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'uri' => $contact['url']]); DI::logger()->debug('Account detected', ['uri-id' => $media['uri-id'], 'url' => $media['url'], 'uri' => $contact['url']]);
return $media; return $media;
@ -439,6 +449,9 @@ class Media
$media['publisher-url'] = $data['publisher_url'] ?? null; $media['publisher-url'] = $data['publisher_url'] ?? null;
$media['publisher-name'] = $data['publisher_name'] ?? null; $media['publisher-name'] = $data['publisher_name'] ?? null;
$media['publisher-image'] = $data['publisher_img'] ?? null; $media['publisher-image'] = $data['publisher_img'] ?? null;
$media['language'] = $data['language'] ?? null;
$media['published'] = $data['published'] ?? null;
$media['modified'] = $data['modified'] ?? null;
return $media; return $media;
} }

View file

@ -28,7 +28,7 @@ class Relationships extends BaseApi
], $request); ], $request);
if (empty($request['id'])) { if (empty($request['id'])) {
$this->logAndJsonError(422, $this->errorFactory->UnprocessableEntity()); $this->jsonExit([]);
} }
if (!is_array($request['id'])) { if (!is_array($request['id'])) {

View file

@ -50,6 +50,10 @@ class Search extends BaseApi
$limit = min($request['limit'], 40); $limit = min($request['limit'], 40);
if (Network::isValidHttpUrl($request['q']) && ($request['offset'] == 0)) {
$this->searchLinks($uid, $request['q'], $request['type']);
}
$result = ['accounts' => [], 'statuses' => [], 'hashtags' => []]; $result = ['accounts' => [], 'statuses' => [], 'hashtags' => []];
if (empty($request['type']) || ($request['type'] == 'accounts')) { if (empty($request['type']) || ($request['type'] == 'accounts')) {
@ -79,6 +83,43 @@ class Search extends BaseApi
$this->jsonExit($result); $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 int $uid
* @param string $q * @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) private function searchAccounts(int $uid, string $q, bool $resolve, int $limit, int $offset, bool $following)
{ {
if (($offset == 0) && (strrpos($q, '@') > 0 || Network::isValidHttpUrl($q)) if (($offset == 0) && (strrpos($q, '@') > 0) && $id = Contact::getIdForURL($q, 0, $resolve ? null : false)) {
&& $id = Contact::getIdForURL($q, 0, $resolve ? null : false)
) {
return DI::mstdnAccount()->createFromContactId($id, $uid); 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) 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]]; $params = ['order' => ['uri-id' => true], 'limit' => [$offset, $limit]];
if (substr($q, 0, 1) == '#') { if (substr($q, 0, 1) == '#') {

View file

@ -38,6 +38,8 @@ class Account extends BaseDataTransferObject
/** @var bool */ /** @var bool */
protected $discoverable; protected $discoverable;
/** @var bool */ /** @var bool */
protected $indexable;
/** @var bool */
protected $group; protected $group;
/** @var string|null (Datetime) */ /** @var string|null (Datetime) */
protected $created_at; protected $created_at;
@ -45,6 +47,8 @@ class Account extends BaseDataTransferObject
protected $note; protected $note;
/** @var string (URL)*/ /** @var string (URL)*/
protected $url; protected $url;
/** @var string (URL)*/
protected $uri;
/** @var string (URL) */ /** @var string (URL) */
protected $avatar; protected $avatar;
/** @var string (URL) */ /** @var string (URL) */
@ -61,6 +65,8 @@ class Account extends BaseDataTransferObject
protected $statuses_count; protected $statuses_count;
/** @var string|null (Datetime) */ /** @var string|null (Datetime) */
protected $last_status_at = null; protected $last_status_at = null;
/** @var bool */
protected $hide_collections = false;
/** @var Emoji[] */ /** @var Emoji[] */
protected $emojis; protected $emojis;
/** @var Account|null */ /** @var Account|null */
@ -78,36 +84,37 @@ class Account extends BaseDataTransferObject
*/ */
public function __construct(BaseURL $baseUrl, array $account, Fields $fields) public function __construct(BaseURL $baseUrl, array $account, Fields $fields)
{ {
$this->id = (string)$account['pid']; $this->id = (string)$account['pid'];
$this->username = $account['nick']; $this->username = $account['nick'];
$this->acct = $this->acct = strpos($account['url'], $baseUrl . '/') === 0 ?
strpos($account['url'], $baseUrl . '/') === 0 ?
$account['nick'] : $account['nick'] :
$account['addr']; $account['addr'];
$this->display_name = $account['name']; $this->display_name = $account['name'];
$this->locked = (bool)$account['manually-approve']; $this->locked = (bool)$account['manually-approve'];
$this->bot = ($account['contact-type'] == Contact::TYPE_NEWS); $this->bot = ($account['contact-type'] == Contact::TYPE_NEWS);
$this->discoverable = !$account['unsearchable']; $this->discoverable = !$account['unsearchable'];
$this->group = ($account['contact-type'] == Contact::TYPE_COMMUNITY); $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->created_at = DateTimeFormat::utc($account['created'] ?: DBA::NULL_DATETIME, DateTimeFormat::JSON);
$this->note = BBCode::convertForUriId($account['uri-id'], $account['about'], BBCode::EXTERNAL); $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 = 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->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'] ?? ''); $this->header = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? '');
$this->header_static = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? '', true); $this->header_static = Contact::getHeaderUrlForId($account['id'] ?? 0 ?: $account['pid'], '', $account['updated'], $account['guid'] ?? '', true);
$this->followers_count = $account['ap-followers_count'] ?? $account['diaspora-interacted_count'] ?? 0; $this->followers_count = $account['ap-followers_count'] ?? $account['diaspora-interacted_count'] ?? 0;
$this->following_count = $account['ap-following_count'] ?? $account['diaspora-interacting_count'] ?? 0; $this->following_count = $account['ap-following_count'] ?? $account['diaspora-interacting_count'] ?? 0;
$this->statuses_count = $account['ap-statuses_count'] ?? $account['diaspora-post_count'] ?? 0; $this->statuses_count = $account['ap-statuses_count'] ?? $account['diaspora-post_count'] ?? 0;
$lastItem = $account['last-item'] ? DateTimeFormat::utc($account['last-item'], 'Y-m-d') : DBA::NULL_DATETIME; $lastItem = $account['last-item'] ? DateTimeFormat::utc($account['last-item'], 'Y-m-d') : DBA::NULL_DATETIME;
$this->last_status_at = $lastItem != DBA::NULL_DATETIME ? DateTimeFormat::utc($lastItem, DateTimeFormat::JSON) : null; $this->last_status_at = $lastItem != DBA::NULL_DATETIME ? DateTimeFormat::utc($lastItem, DateTimeFormat::JSON) : null;
// No custom emojis per account in Friendica // No custom emojis per account in Friendica
$this->emojis = []; $this->emojis = [];
$this->fields = $fields->getArrayCopy(); $this->fields = $fields->getArrayCopy();
} }
/** /**

View file

@ -63,8 +63,8 @@ class Application extends BaseDataTransferObject
unset($application['redirect_uri']); unset($application['redirect_uri']);
} }
if (empty($application['website'])) { if (empty($application['vapid_key'])) {
unset($application['website']); unset($application['vapid_key']);
} }
return $application; return $application;

View file

@ -27,6 +27,8 @@ class Attachment extends BaseDataTransferObject
/** @var string */ /** @var string */
protected $remote_url; protected $remote_url;
/** @var string */ /** @var string */
protected $preview_remote_url;
/** @var string */
protected $text_url; protected $text_url;
/** @var string */ /** @var string */
protected $description; protected $description;
@ -52,7 +54,6 @@ class Attachment extends BaseDataTransferObject
$this->url = $url; $this->url = $url;
$this->preview_url = $preview; $this->preview_url = $preview;
$this->remote_url = $remote; $this->remote_url = $remote;
$this->text_url = $this->remote_url ?? $this->url;
$this->description = $attachment['description']; $this->description = $attachment['description'];
$this->blurhash = $attachment['blurhash']; $this->blurhash = $attachment['blurhash'];
if ($type === 'image') { if ($type === 'image') {

View file

@ -8,6 +8,7 @@
namespace Friendica\Object\Api\Mastodon; namespace Friendica\Object\Api\Mastodon;
use Friendica\BaseDataTransferObject; use Friendica\BaseDataTransferObject;
use Friendica\Util\DateTimeFormat;
/** /**
* Class Card * Class Card
@ -23,6 +24,8 @@ class Card extends BaseDataTransferObject
/** @var string */ /** @var string */
protected $description; protected $description;
/** @var string */ /** @var string */
protected $language;
/** @var string */
protected $type; protected $type;
/** @var string */ /** @var string */
protected $author_name; protected $author_name;
@ -41,9 +44,15 @@ class Card extends BaseDataTransferObject
/** @var string */ /** @var string */
protected $image; protected $image;
/** @var string */ /** @var string */
protected $image_description = '';
/** @var string */
protected $embed_url; protected $embed_url;
/** @var string */ /** @var string */
protected $blurhash; protected $blurhash;
/** @var string|null (Datetime) */
protected $published_at;
/** @var array */
protected $authors = [];
/** @var array */ /** @var array */
protected $history; protected $history;
@ -55,20 +64,22 @@ class Card extends BaseDataTransferObject
*/ */
public function __construct(array $attachment, array $history = []) public function __construct(array $attachment, array $history = [])
{ {
$this->url = $attachment['url'] ?? ''; $this->url = $attachment['url'] ?? '';
$this->title = $attachment['title'] ?? ''; $this->title = $attachment['title'] ?? '';
$this->description = $attachment['description'] ?? ''; $this->description = $attachment['description'] ?? '';
$this->type = $attachment['type'] ?? ''; $this->language = $attachment['language'] ?? '';
$this->author_name = $attachment['author_name'] ?? ''; $this->type = $attachment['type'] ?? '';
$this->author_url = $attachment['author_url'] ?? ''; $this->author_name = $attachment['author_name'] ?? '';
$this->author_url = $attachment['author_url'] ?? '';
$this->provider_name = $attachment['provider_name'] ?? ''; $this->provider_name = $attachment['provider_name'] ?? '';
$this->provider_url = $attachment['provider_url'] ?? ''; $this->provider_url = $attachment['provider_url'] ?? '';
$this->html = ''; $this->html = '';
$this->width = $attachment['width'] ?? 0; $this->width = $attachment['width'] ?? 0;
$this->height = $attachment['height'] ?? 0; $this->height = $attachment['height'] ?? 0;
$this->image = $attachment['image'] ?? ''; $this->image = $attachment['image'] ?? '';
$this->embed_url = ''; $this->embed_url = '';
$this->blurhash = $attachment['blurhash'] ?? ''; $this->blurhash = $attachment['blurhash'] ?? '';
$this->published_at = !empty($attachment['published']) ? DateTimeFormat::utc($attachment['published'], DateTimeFormat::JSON) : null;
$this->history = $history; $this->history = $history;
} }
@ -83,6 +94,10 @@ class Card extends BaseDataTransferObject
return []; return [];
} }
if (empty($this->history)) {
unset($this->history);
}
return parent::toArray(); return parent::toArray();
} }
} }

View file

@ -46,15 +46,19 @@ class Relationship extends BaseDataTransferObject
protected $blocking = false; protected $blocking = false;
/** @var bool */ /** @var bool */
protected $domain_blocking = false; protected $domain_blocking = false;
/** @var bool */
protected $blocked_by = false;
/**
* Unsupported
* @var array
*/
protected $languages = [];
/** /**
* Unsupported * Unsupported
* @var bool * @var bool
*/ */
protected $blocked_by = false; protected $requested_by = false;
/** /** @var string */
* Unsupported
* @var string
*/
protected $note = ''; protected $note = '';
/** /**
@ -63,7 +67,7 @@ class Relationship extends BaseDataTransferObject
* @param bool $blocked "true" if user is blocked * @param bool $blocked "true" if user is blocked
* @param bool $muted "true" if user is muted * @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->id = (string)$contactId;
$this->following = false; $this->following = false;
@ -86,6 +90,7 @@ class Relationship extends BaseDataTransferObject
$this->muting = (bool)($contactRecord['readonly'] ?? false) || $muted; $this->muting = (bool)($contactRecord['readonly'] ?? false) || $muted;
$this->notifying = (bool)$contactRecord['notify_new_posts'] ?? false; $this->notifying = (bool)$contactRecord['notify_new_posts'] ?? false;
$this->blocking = (bool)($contactRecord['blocked'] ?? false) || $blocked; $this->blocking = (bool)($contactRecord['blocked'] ?? false) || $blocked;
$this->blocked_by = $isBlocked;
$this->note = $contactRecord['info']; $this->note = $contactRecord['info'];
} }

View file

@ -64,6 +64,8 @@ class Status extends BaseDataTransferObject
protected $pinned = false; protected $pinned = false;
/** @var string */ /** @var string */
protected $content; protected $content;
/** @var array */
protected $filtered = [];
/** @var Status|null */ /** @var Status|null */
protected $reblog = null; protected $reblog = null;
/** @var Status|null - Akkoma extension, see issue https://github.com/friendica/friendica/issues/12603 */ /** @var Status|null - Akkoma extension, see issue https://github.com/friendica/friendica/issues/12603 */
@ -95,10 +97,10 @@ class Status extends BaseDataTransferObject
*/ */
public function __construct(array $item, Account $account, Counts $counts, UserAttributes $userAttributes, bool $sensitive, Application $application, array $mentions, array $tags, Card $card, array $attachments, array $in_reply, array $reblog, FriendicaExtension $friendica, array $quote = null, array $poll = null, array $emojis = null) public function __construct(array $item, Account $account, Counts $counts, UserAttributes $userAttributes, bool $sensitive, Application $application, array $mentions, array $tags, Card $card, array $attachments, array $in_reply, array $reblog, FriendicaExtension $friendica, array $quote = null, array $poll = null, array $emojis = null)
{ {
$reblogged = !empty($reblog); $reblogged = !empty($reblog);
$this->id = (string)$item['uri-id']; $this->id = (string)$item['uri-id'];
$this->created_at = DateTimeFormat::utc($item['created'], DateTimeFormat::JSON); $this->created_at = DateTimeFormat::utc($item['created'], DateTimeFormat::JSON);
$this->edited_at = DateTimeFormat::utc($item['edited'], DateTimeFormat::JSON); $this->edited_at = DateTimeFormat::utc($item['edited'], DateTimeFormat::JSON);
if ($item['gravity'] == Item::GRAVITY_COMMENT) { if ($item['gravity'] == Item::GRAVITY_COMMENT) {
$this->in_reply_to_id = (string)$item['thr-parent-id']; $this->in_reply_to_id = (string)$item['thr-parent-id'];
@ -109,7 +111,7 @@ class Status extends BaseDataTransferObject
$this->sensitive = $sensitive; $this->sensitive = $sensitive;
$this->spoiler_text = $item['title'] ?: $item['content-warning'] ?: ''; $this->spoiler_text = $item['title'] ?: $item['content-warning'] ?: '';
$visibility = ['public', 'private', 'unlisted']; $visibility = ['public', 'private', 'unlisted'];
$this->visibility = $visibility[$item['private']]; $this->visibility = $visibility[$item['private']];
$languages = json_decode($item['language'] ?? '', true); $languages = json_decode($item['language'] ?? '', true);
@ -120,28 +122,28 @@ class Status extends BaseDataTransferObject
$this->language = null; $this->language = null;
} }
$this->uri = $item['uri']; $this->uri = $item['uri'];
$this->url = $item['plink'] ?? null; $this->url = $item['plink'] ?? null;
$this->replies_count = $reblogged ? 0 : $counts->replies; $this->replies_count = $reblogged ? 0 : $counts->replies;
$this->reblogs_count = $reblogged ? 0 : $counts->reblogs; $this->reblogs_count = $reblogged ? 0 : $counts->reblogs;
$this->favourites_count = $reblogged ? 0 : $counts->favourites; $this->favourites_count = $reblogged ? 0 : $counts->favourites;
$this->favourited = $userAttributes->favourited; $this->favourited = $userAttributes->favourited;
$this->reblogged = $userAttributes->reblogged; $this->reblogged = $userAttributes->reblogged;
$this->muted = $userAttributes->muted; $this->muted = $userAttributes->muted;
$this->bookmarked = $userAttributes->bookmarked; $this->bookmarked = $userAttributes->bookmarked;
$this->pinned = $userAttributes->pinned; $this->pinned = $userAttributes->pinned;
$this->content = $reblogged ? '' : BBCode::convertForUriId($item['uri-id'], BBCode::setMentionsToNicknames($item['raw-body'] ?? $item['body']), BBCode::MASTODON_API); $this->content = $reblogged ? '' : BBCode::convertForUriId($item['uri-id'], BBCode::setMentionsToNicknames($item['raw-body'] ?? $item['body']), BBCode::MASTODON_API);
$this->reblog = $reblog; $this->reblog = $reblog;
$this->quote = $quote; $this->quote = $quote;
$this->application = $application->toArray(); $this->application = $application->toArray();
$this->account = $account->toArray(); $this->account = $account->toArray();
$this->media_attachments = $reblogged ? [] : $attachments; $this->media_attachments = $reblogged ? [] : $attachments;
$this->mentions = $reblogged ? [] : $mentions; $this->mentions = $reblogged ? [] : $mentions;
$this->tags = $reblogged ? [] : $tags; $this->tags = $reblogged ? [] : $tags;
$this->emojis = $reblogged ? [] : ($emojis ?: []); $this->emojis = $reblogged ? [] : ($emojis ?: []);
$this->card = $reblogged ? null : ($card->toArray() ?: null); $this->card = $reblogged ? null : ($card->toArray() ?: null);
$this->poll = $reblogged ? null : $poll; $this->poll = $reblogged ? null : $poll;
$this->friendica = $reblogged ? null : $friendica; $this->friendica = $reblogged ? null : $friendica;
} }
/** /**

View file

@ -42,4 +42,13 @@ class Tag extends BaseDataTransferObject
$this->history = $history; $this->history = $history;
$this->following = $following; $this->following = $following;
} }
public function toArray(): array
{
if (empty($this->history)) {
unset($this->history);
}
return parent::toArray();
}
} }

View file

@ -296,6 +296,17 @@ class ParseUrl
$xpath = new DOMXPath($doc); $xpath = new DOMXPath($doc);
$list = $xpath->query('//html[@lang]');
foreach ($list as $node) {
if ($node->attributes->length) {
foreach ($node->attributes as $attribute) {
if ($attribute->name == 'lang') {
$siteinfo['language'] = $attribute->value;
}
}
}
}
$list = $xpath->query('//meta[@content]'); $list = $xpath->query('//meta[@content]');
foreach ($list as $node) { foreach ($list as $node) {
$meta_tag = []; $meta_tag = [];
@ -495,6 +506,10 @@ class ParseUrl
} }
} }
if (!empty($siteinfo['language'])) {
$siteinfo['language'] = explode('_', str_replace('-', '_', $siteinfo['language']))[0];
}
DI::logger()->info('Siteinfo fetched', ['url' => $url, 'siteinfo' => $siteinfo]); DI::logger()->info('Siteinfo fetched', ['url' => $url, 'siteinfo' => $siteinfo]);
Hook::callAll('getsiteinfo', $siteinfo); Hook::callAll('getsiteinfo', $siteinfo);

View file

@ -44,7 +44,7 @@ use Friendica\Database\DBA;
// This file is required several times during the test in DbaDefinition which justifies this condition // This file is required several times during the test in DbaDefinition which justifies this condition
if (!defined('DB_UPDATE_VERSION')) { if (!defined('DB_UPDATE_VERSION')) {
define('DB_UPDATE_VERSION', 1579); define('DB_UPDATE_VERSION', 1580);
} }
return [ return [
@ -1447,6 +1447,9 @@ return [
"publisher-url" => ["type" => "varbinary(383)", "comment" => "URL of the publisher of the media"], "publisher-url" => ["type" => "varbinary(383)", "comment" => "URL of the publisher of the media"],
"publisher-name" => ["type" => "varchar(255)", "comment" => "Name of the publisher of the media"], "publisher-name" => ["type" => "varchar(255)", "comment" => "Name of the publisher of the media"],
"publisher-image" => ["type" => "varbinary(383)", "comment" => "Image of the publisher of the media"], "publisher-image" => ["type" => "varbinary(383)", "comment" => "Image of the publisher of the media"],
"language" => ["type" => "char(3)", "comment" => "Language information about this media in the ISO 639 format"],
"published" => ["type" => "datetime", "comment" => "Publification date of this media"],
"modified" => ["type" => "datetime", "comment" => "Modification date of this media"],
], ],
"indexes" => [ "indexes" => [
"PRIMARY" => ["id"], "PRIMARY" => ["id"],