From 9ed8479b32586f7b22671131de06d10bb522c472 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 25 Dec 2019 05:38:50 -0500 Subject: [PATCH 1/6] Rename Api\Mastodon\Account::create contact parameter to publicContact - Update Mastodon entity documentation link - Add default value for fields --- src/Api/Mastodon/Account.php | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Api/Mastodon/Account.php b/src/Api/Mastodon/Account.php index 18ab93be0c..2a2212482b 100644 --- a/src/Api/Mastodon/Account.php +++ b/src/Api/Mastodon/Account.php @@ -10,7 +10,7 @@ use Friendica\Util\DateTimeFormat; /** * Class Account * - * @see https://docs.joinmastodon.org/api/entities/#account + * @see https://docs.joinmastodon.org/entities/account */ class Account { @@ -54,35 +54,37 @@ class Account var $bot = null; /** - * Creates an account record from a contact record. Expects all contact table fields to be set + * Creates an account record from a public contact record. Expects all contact table fields to be set. * - * @param array $contact Full contact table record - * @param array $apcontact Full apcontact table record + * @param array $publicContact Full contact table record with uid = 0 + * @param array $apcontact Optional full apcontact table record * @return Account * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function createFromContact(array $contact, array $apcontact = []) + public static function createFromContact(array $publicContact, array $apcontact = []) { $account = new Account(); - $account->id = $contact['id']; - $account->username = $contact['nick']; - $account->acct = $contact['nick']; - $account->display_name = $contact['name']; + $account->id = $publicContact['id']; + $account->username = $publicContact['nick']; + $account->acct = $publicContact['addr']; + $account->display_name = $publicContact['name']; $account->locked = !empty($apcontact['manually-approve']); - $account->created_at = DateTimeFormat::utc($contact['created'], DateTimeFormat::ATOM); + $account->created_at = DateTimeFormat::utc($publicContact['created'], DateTimeFormat::ATOM); $account->followers_count = $apcontact['followers_count'] ?? 0; $account->following_count = $apcontact['following_count'] ?? 0; $account->statuses_count = $apcontact['statuses_count'] ?? 0; - $account->note = BBCode::convert($contact['about'], false); - $account->url = $contact['url']; - $account->avatar = $contact['avatar']; - $account->avatar_static = $contact['avatar']; + $account->note = BBCode::convert($publicContact['about'], false); + $account->url = $publicContact['url']; + $account->avatar = $publicContact['avatar']; + $account->avatar_static = $publicContact['avatar']; // No header picture in Friendica $account->header = ''; $account->header_static = ''; // No custom emojis per account in Friendica $account->emojis = []; - $account->bot = ($contact['contact-type'] == Contact::TYPE_NEWS); + // No metadata fields in Friendica + $account->fields = []; + $account->bot = ($publicContact['contact-type'] == Contact::TYPE_NEWS); return $account; } From cb7875b0356621e9ceff2fe7f167946d5cefe080 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 25 Dec 2019 05:41:35 -0500 Subject: [PATCH 2/6] Rename Api\Mastodon\Account::createFromContact to just create - Retrieve public contact in Api\Mastodon\FollowRequests instead of user contact --- src/Api/Mastodon/Account.php | 2 +- src/Api/Mastodon/Instance.php | 10 +++++----- src/Module/Api/Mastodon/FollowRequests.php | 11 ++++++++--- src/Module/Api/Mastodon/Instance.php | 2 +- 4 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/Api/Mastodon/Account.php b/src/Api/Mastodon/Account.php index 2a2212482b..ca6efcb573 100644 --- a/src/Api/Mastodon/Account.php +++ b/src/Api/Mastodon/Account.php @@ -61,7 +61,7 @@ class Account * @return Account * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function createFromContact(array $publicContact, array $apcontact = []) + public static function create(array $publicContact, array $apcontact = []) { $account = new Account(); $account->id = $publicContact['id']; diff --git a/src/Api/Mastodon/Instance.php b/src/Api/Mastodon/Instance.php index 73541ee237..6d1dfb7337 100644 --- a/src/Api/Mastodon/Instance.php +++ b/src/Api/Mastodon/Instance.php @@ -3,8 +3,6 @@ namespace Friendica\Api\Mastodon; use Friendica\App; -use Friendica\Api\Mastodon\Account; -use Friendica\Api\Mastodon\Stats; use Friendica\Core\Config; use Friendica\Database\DBA; use Friendica\DI; @@ -58,15 +56,17 @@ class Instance { $register_policy = intval(Config::get('config', 'register_policy')); + $baseUrl = DI::baseUrl(); + $instance = new Instance(); - $instance->uri = DI::baseUrl()->get(); + $instance->uri = $baseUrl->get(); $instance->title = Config::get('config', 'sitename'); $instance->description = Config::get('config', 'info'); $instance->email = Config::get('config', 'admin_email'); $instance->version = FRIENDICA_VERSION; $instance->urls = []; // Not supported $instance->stats = Stats::get(); - $instance->thumbnail = DI::baseUrl()->get() . (Config::get('system', 'shortcut_icon') ?? 'images/friendica-32.png'); + $instance->thumbnail = $baseUrl->get() . (Config::get('system', 'shortcut_icon') ?? 'images/friendica-32.png'); $instance->languages = [Config::get('system', 'language')]; $instance->max_toot_chars = (int)Config::get('config', 'api_import_size', Config::get('config', 'max_import_size')); $instance->registrations = ($register_policy != Register::CLOSED); @@ -79,7 +79,7 @@ class Instance if (!empty($administrator)) { $adminContact = DBA::selectFirst('contact', [], ['nick' => $administrator['nickname'], 'self' => true]); $apcontact = APContact::getByURL($adminContact['url'], false); - $instance->contact_account = Account::createFromContact($adminContact, $apcontact); + $instance->contact_account = Account::create($adminContact, $apcontact); } } diff --git a/src/Module/Api/Mastodon/FollowRequests.php b/src/Module/Api/Mastodon/FollowRequests.php index 6cafb44a66..304f78767f 100644 --- a/src/Module/Api/Mastodon/FollowRequests.php +++ b/src/Module/Api/Mastodon/FollowRequests.php @@ -87,9 +87,14 @@ class FollowRequests extends Api $return = []; foreach ($intros as $intro) { - $contact = Contact::getById($intro['contact-id']); - $apcontact = APContact::getByURL($contact['url'], false); - $account = Mastodon\Account::createFromContact($contact, $apcontact); + $cdata = Contact::getPublicAndUserContacID($intro['contact-id'], $intro['uid']); + if (empty($cdata['public'])) { + continue; + } + + $publicContact = Contact::getById($cdata['public']); + $apcontact = APContact::getByURL($publicContact['url'], false); + $account = Mastodon\Account::create($publicContact, $apcontact); // Not ideal, the same "account" can have multiple ids depending on the context $account->id = $intro['id']; diff --git a/src/Module/Api/Mastodon/Instance.php b/src/Module/Api/Mastodon/Instance.php index 559fa68cc4..96d4db3302 100644 --- a/src/Module/Api/Mastodon/Instance.php +++ b/src/Module/Api/Mastodon/Instance.php @@ -13,7 +13,7 @@ class Instance extends Api { /** * @param array $parameters - * @throws HTTPException\InternalServerErrorException + * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ public static function rawContent(array $parameters = []) { From bef16702c77dc1ac69bf362d0f7420139252e365 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 25 Dec 2019 05:42:46 -0500 Subject: [PATCH 3/6] Add missing fields to Mastodon API Account entity --- src/Api/Mastodon/Account.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/Api/Mastodon/Account.php b/src/Api/Mastodon/Account.php index ca6efcb573..0a86be7e7b 100644 --- a/src/Api/Mastodon/Account.php +++ b/src/Api/Mastodon/Account.php @@ -52,6 +52,12 @@ class Account var $fields = null; /** @var bool|null */ var $bot = null; + /** @var bool */ + var $group; + /** @var bool */ + var $discoverable; + /** @var string|null (Datetime) */ + var $last_status_at = null; /** * Creates an account record from a public contact record. Expects all contact table fields to be set. @@ -85,6 +91,9 @@ class Account // No metadata fields in Friendica $account->fields = []; $account->bot = ($publicContact['contact-type'] == Contact::TYPE_NEWS); + $account->group = ($publicContact['contact-type'] == Contact::TYPE_COMMUNITY); + $account->discoverable = !$publicContact['unsearchable']; + $account->last_status_at = !empty($publicContact['last-item']) ? DateTimeFormat::utc($publicContact['last-item'], DateTimeFormat::ATOM) : null; return $account; } From c286772fb5633aec0b6927d34fe7903c66cce28f Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Wed, 25 Dec 2019 05:58:54 -0500 Subject: [PATCH 4/6] Correct value of Mastodon API Account acct field for local users --- src/Api/Mastodon/Account.php | 13 +++++++++---- src/Api/Mastodon/Instance.php | 2 +- src/Module/Api/Mastodon/FollowRequests.php | 22 ++++++++++++++++------ 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/Api/Mastodon/Account.php b/src/Api/Mastodon/Account.php index 0a86be7e7b..077b02f82d 100644 --- a/src/Api/Mastodon/Account.php +++ b/src/Api/Mastodon/Account.php @@ -2,6 +2,7 @@ namespace Friendica\Api\Mastodon; +use Friendica\App\BaseURL; use Friendica\Content\Text\BBCode; use Friendica\Database\DBA; use Friendica\Model\Contact; @@ -62,17 +63,21 @@ class Account /** * Creates an account record from a public contact record. Expects all contact table fields to be set. * - * @param array $publicContact Full contact table record with uid = 0 - * @param array $apcontact Optional full apcontact table record + * @param BaseURL $baseUrl + * @param array $publicContact Full contact table record with uid = 0 + * @param array $apcontact Optional full apcontact table record * @return Account * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function create(array $publicContact, array $apcontact = []) + public static function create(BaseURL $baseUrl, array $publicContact, array $apcontact = []) { $account = new Account(); $account->id = $publicContact['id']; $account->username = $publicContact['nick']; - $account->acct = $publicContact['addr']; + $account->acct = + strpos($publicContact['url'], $baseUrl->get() . '/') === 0 ? + $publicContact['nick'] : + $publicContact['addr']; $account->display_name = $publicContact['name']; $account->locked = !empty($apcontact['manually-approve']); $account->created_at = DateTimeFormat::utc($publicContact['created'], DateTimeFormat::ATOM); diff --git a/src/Api/Mastodon/Instance.php b/src/Api/Mastodon/Instance.php index 6d1dfb7337..6652641b49 100644 --- a/src/Api/Mastodon/Instance.php +++ b/src/Api/Mastodon/Instance.php @@ -79,7 +79,7 @@ class Instance if (!empty($administrator)) { $adminContact = DBA::selectFirst('contact', [], ['nick' => $administrator['nickname'], 'self' => true]); $apcontact = APContact::getByURL($adminContact['url'], false); - $instance->contact_account = Account::create($adminContact, $apcontact); + $instance->contact_account = Account::create($baseUrl, $adminContact, $apcontact); } } diff --git a/src/Module/Api/Mastodon/FollowRequests.php b/src/Module/Api/Mastodon/FollowRequests.php index 304f78767f..e1e70577b8 100644 --- a/src/Module/Api/Mastodon/FollowRequests.php +++ b/src/Module/Api/Mastodon/FollowRequests.php @@ -26,6 +26,15 @@ class FollowRequests extends Api } } + /** + * @param array $parameters + * @throws HTTPException\BadRequestException + * @throws HTTPException\ForbiddenException + * @throws HTTPException\NotFoundException + * @throws HTTPException\UnauthorizedException + * @see https://docs.joinmastodon.org/methods/accounts/follow_requests#accept-follow + * @see https://docs.joinmastodon.org/methods/accounts/follow_requests#reject-follow + */ public static function post(array $parameters = []) { parent::post($parameters); @@ -58,7 +67,8 @@ class FollowRequests extends Api /** * @param array $parameters * @throws HTTPException\InternalServerErrorException - * @see https://docs.joinmastodon.org/api/rest/follow-requests/#get-api-v1-follow-requests + * @throws \ImagickException + * @see https://docs.joinmastodon.org/methods/accounts/follow_requests#pending-follows */ public static function rawContent(array $parameters = []) { @@ -66,6 +76,8 @@ class FollowRequests extends Api $max_id = $_GET['max_id'] ?? null; $limit = intval($_GET['limit'] ?? 40); + $baseUrl = DI::baseUrl(); + if (isset($since_id) && isset($max_id)) { $condition = ['`uid` = ? AND NOT `ignore` AND `id` > ? AND `id` < ?', self::$current_user_id, $since_id, $max_id]; } elseif (isset($since_id)) { @@ -94,7 +106,7 @@ class FollowRequests extends Api $publicContact = Contact::getById($cdata['public']); $apcontact = APContact::getByURL($publicContact['url'], false); - $account = Mastodon\Account::create($publicContact, $apcontact); + $account = Mastodon\Account::create($baseUrl, $publicContact, $apcontact); // Not ideal, the same "account" can have multiple ids depending on the context $account->id = $intro['id']; @@ -107,13 +119,11 @@ class FollowRequests extends Api $base_query['limit'] = $limit; } - $BaseURL = DI::baseUrl(); - $links = []; if ($count > $limit) { - $links[] = '<' . $BaseURL->get() . '/api/v1/follow_requests?' . http_build_query($base_query + ['max_id' => $intros[count($intros) - 1]['id']]) . '>; rel="next"'; + $links[] = '<' . $baseUrl->get() . '/api/v1/follow_requests?' . http_build_query($base_query + ['max_id' => $intros[count($intros) - 1]['id']]) . '>; rel="next"'; } - $links[] = '<' . $BaseURL->get() . '/api/v1/follow_requests?' . http_build_query($base_query + ['since_id' => $intros[0]['id']]) . '>; rel="prev"'; + $links[] = '<' . $baseUrl->get() . '/api/v1/follow_requests?' . http_build_query($base_query + ['since_id' => $intros[0]['id']]) . '>; rel="prev"'; header('Link: ' . implode(', ', $links)); From 0dbce6e58ba6494daf1ad30812cdffd5a779eda4 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Thu, 26 Dec 2019 20:06:28 -0500 Subject: [PATCH 5/6] Add user contact data superseding to Mastodon\Account::create --- src/Api/Mastodon/Account.php | 11 +++++++---- src/Module/Api/Mastodon/FollowRequests.php | 3 ++- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/Api/Mastodon/Account.php b/src/Api/Mastodon/Account.php index 077b02f82d..389a13dd3a 100644 --- a/src/Api/Mastodon/Account.php +++ b/src/Api/Mastodon/Account.php @@ -66,10 +66,11 @@ class Account * @param BaseURL $baseUrl * @param array $publicContact Full contact table record with uid = 0 * @param array $apcontact Optional full apcontact table record + * @param array $userContact Optional full contact table record with uid = local_user() * @return Account * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function create(BaseURL $baseUrl, array $publicContact, array $apcontact = []) + public static function create(BaseURL $baseUrl, array $publicContact, array $apcontact = [], array $userContact = []) { $account = new Account(); $account->id = $publicContact['id']; @@ -86,8 +87,8 @@ class Account $account->statuses_count = $apcontact['statuses_count'] ?? 0; $account->note = BBCode::convert($publicContact['about'], false); $account->url = $publicContact['url']; - $account->avatar = $publicContact['avatar']; - $account->avatar_static = $publicContact['avatar']; + $account->avatar = $userContact['avatar'] ?? $publicContact['avatar']; + $account->avatar_static = $userContact['avatar'] ?? $publicContact['avatar']; // No header picture in Friendica $account->header = ''; $account->header_static = ''; @@ -98,7 +99,9 @@ class Account $account->bot = ($publicContact['contact-type'] == Contact::TYPE_NEWS); $account->group = ($publicContact['contact-type'] == Contact::TYPE_COMMUNITY); $account->discoverable = !$publicContact['unsearchable']; - $account->last_status_at = !empty($publicContact['last-item']) ? DateTimeFormat::utc($publicContact['last-item'], DateTimeFormat::ATOM) : null; + + $last_item = $userContact['last-item'] ?? $publicContact['last-item']; + $account->last_status_at = !empty($last_item) ? DateTimeFormat::utc($last_item, DateTimeFormat::ATOM) : null; return $account; } diff --git a/src/Module/Api/Mastodon/FollowRequests.php b/src/Module/Api/Mastodon/FollowRequests.php index e1e70577b8..e31f023cde 100644 --- a/src/Module/Api/Mastodon/FollowRequests.php +++ b/src/Module/Api/Mastodon/FollowRequests.php @@ -105,8 +105,9 @@ class FollowRequests extends Api } $publicContact = Contact::getById($cdata['public']); + $userContact = Contact::getById($cdata['user']); $apcontact = APContact::getByURL($publicContact['url'], false); - $account = Mastodon\Account::create($baseUrl, $publicContact, $apcontact); + $account = Mastodon\Account::create($baseUrl, $publicContact, $apcontact, $userContact); // Not ideal, the same "account" can have multiple ids depending on the context $account->id = $intro['id']; From 4aa01ee17f1c6b45e8be2e8b0e692a2d1a232de8 Mon Sep 17 00:00:00 2001 From: Hypolite Petovan Date: Fri, 27 Dec 2019 12:23:11 -0500 Subject: [PATCH 6/6] Use most recent last item date in Mastodon\Account --- src/Api/Mastodon/Account.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Api/Mastodon/Account.php b/src/Api/Mastodon/Account.php index 389a13dd3a..ade6fc03dc 100644 --- a/src/Api/Mastodon/Account.php +++ b/src/Api/Mastodon/Account.php @@ -100,8 +100,11 @@ class Account $account->group = ($publicContact['contact-type'] == Contact::TYPE_COMMUNITY); $account->discoverable = !$publicContact['unsearchable']; - $last_item = $userContact['last-item'] ?? $publicContact['last-item']; - $account->last_status_at = !empty($last_item) ? DateTimeFormat::utc($last_item, DateTimeFormat::ATOM) : null; + $publicContactLastItem = $publicContact['last-item'] ?: DBA::NULL_DATETIME; + $userContactLastItem = $userContact['last-item'] ?? DBA::NULL_DATETIME; + + $lastItem = $userContactLastItem > $publicContactLastItem ? $userContactLastItem : $publicContactLastItem; + $account->last_status_at = $lastItem != DBA::NULL_DATETIME ? DateTimeFormat::utc($lastItem, DateTimeFormat::ATOM) : null; return $account; }