From 5bb5c44bd9c3063b4f78a74a487353d2ef842cdd Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 09:35:51 +0000 Subject: [PATCH 01/15] API: added account related endpoints --- doc/API-Mastodon.md | 16 ++- src/DI.php | 8 ++ src/Factory/Api/Mastodon/ListEntity.php | 34 ++++++ src/Model/Contact.php | 5 +- .../Api/Mastodon/Accounts/Followers.php | 4 + .../Api/Mastodon/Accounts/Following.php | 4 + src/Module/Api/Mastodon/Accounts/Lists.php | 66 ++++++++++++ src/Module/Api/Mastodon/Accounts/Search.php | 98 +++++++++++++++++ src/Module/Api/Mastodon/Blocks.php | 4 + src/Module/Api/Mastodon/Lists.php | 76 +++++++++++++ src/Module/Api/Mastodon/Lists/Accounts.php | 102 ++++++++++++++++++ src/Module/Api/Mastodon/Mutes.php | 4 + src/Module/Api/Mastodon/Preferences.php | 61 +++++++++++ src/Object/Api/Mastodon/ListEntity.php | 51 +++++++++ src/Object/Api/Mastodon/Preferences.php | 67 ++++++++++++ static/routes.config.php | 12 +-- 16 files changed, 599 insertions(+), 13 deletions(-) create mode 100644 src/Factory/Api/Mastodon/ListEntity.php create mode 100644 src/Module/Api/Mastodon/Accounts/Lists.php create mode 100644 src/Module/Api/Mastodon/Accounts/Search.php create mode 100644 src/Module/Api/Mastodon/Lists.php create mode 100644 src/Module/Api/Mastodon/Lists/Accounts.php create mode 100644 src/Module/Api/Mastodon/Preferences.php create mode 100644 src/Object/Api/Mastodon/ListEntity.php create mode 100644 src/Object/Api/Mastodon/Preferences.php diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 811bb5bc53..18338ac16c 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -15,13 +15,15 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en ## Implemented endpoints -- [`GET /api/v1//accounts/:id`](https://docs.joinmastodon.org/methods/accounts/#retrieve-information) -- [`GET /api/v1//accounts/:id/statuses`](https://docs.joinmastodon.org/methods/accounts/#retrieve-information) -- [`GET /api/v1//accounts/:id/followers`](https://docs.joinmastodon.org/methods/accounts/) -- [`GET /api/v1//accounts/:id/following`](https://docs.joinmastodon.org/methods/accounts/) +- [`GET /api/v1/accounts/:id`](https://docs.joinmastodon.org/methods/accounts/#retrieve-information) +- [`GET /api/v1/accounts/:id/statuses`](https://docs.joinmastodon.org/methods/accounts/#retrieve-information) +- [`GET /api/v1/accounts/:id/followers`](https://docs.joinmastodon.org/methods/accounts/) +- [`GET /api/v1/accounts/:id/following`](https://docs.joinmastodon.org/methods/accounts/) +- [`GET /api/v1/accounts/:id/lists`](https://docs.joinmastodon.org/methods/accounts/) +- [`GET /api/v1/accounts/search`](https://docs.joinmastodon.org/methods/accounts) +- [`GET /api/v1/accounts/verify_credentials`](https://docs.joinmastodon.org/methods/accounts) - [`GET /api/v1/blocks`](https://docs.joinmastodon.org/methods/accounts/blocks/) - [`GET /api/v1/bookmarks`](https://docs.joinmastodon.org/methods/accounts/bookmarks/) -- [`GET /api/v1//accounts/verify_credentials`](https://docs.joinmastodon.org/methods/accounts) - [`GET /api/v1/custom_emojis`](https://docs.joinmastodon.org/methods/instance/custom_emojis/) - Doesn't return unicode emojis since they aren't using an image URL @@ -42,7 +44,11 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - [`GET /api/v1/instance`](https://docs.joinmastodon.org/methods/instance#fetch-instance) - [`GET /api/v1/instance/peers`](https://docs.joinmastodon.org/methods/instance#list-of-connected-domains) +- [`GET /api/v1/lists`](https://docs.joinmastodon.org/methods/timelines/lists/) +- [`GET /api/v1/lists/:id`](https://docs.joinmastodon.org/methods/timelines/lists/) +- [`GET /api/v1/lists/:id/accounts`](https://docs.joinmastodon.org/methods/timelines/lists/) - [`GET /api/v1/mutes`](https://docs.joinmastodon.org/methods/accounts/mutes/) +- [`GET /api/v1/preferences`](https://docs.joinmastodon.org/methods/accounts/preferences/) - [`GET /api/v1/statuses/:id`](https://docs.joinmastodon.org/methods/statuses/) - [`GET /api/v1/statuses/:id/context`](https://docs.joinmastodon.org/methods/statuses/) - [`GET /api/v1/statuses/:id/reblogged_by`](https://docs.joinmastodon.org/methods/statuses/) diff --git a/src/DI.php b/src/DI.php index c2f53e4df9..045425b887 100644 --- a/src/DI.php +++ b/src/DI.php @@ -295,6 +295,14 @@ abstract class DI return self::$dice->create(Factory\Api\Mastodon\Status::class); } + /** + * @return Factory\Api\Mastodon\ListEntity + */ + public static function mstdnList() + { + return self::$dice->create(Factory\Api\Mastodon\ListEntity::class); + } + /** * @return Factory\Api\Mastodon\Mention */ diff --git a/src/Factory/Api/Mastodon/ListEntity.php b/src/Factory/Api/Mastodon/ListEntity.php new file mode 100644 index 0000000000..57547e438a --- /dev/null +++ b/src/Factory/Api/Mastodon/ListEntity.php @@ -0,0 +1,34 @@ +. + * + */ + +namespace Friendica\Factory\Api\Mastodon; + +use Friendica\BaseFactory; +use Friendica\Database\DBA; + +class ListEntity extends BaseFactory +{ + public function create(int $id) + { + $group = DBA::selectFirst('group', ['name'], ['id' => $id, 'deleted' => false]); + return new \Friendica\Object\Api\Mastodon\ListEntity($id, $group['name'] ?? '', 'list'); + } +} diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 411cf333ef..876369f304 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -2763,11 +2763,12 @@ class Contact * * @param string $search Name or nick * @param string $mode Search mode (e.g. "community") + * @param int $uid User ID * * @return array with search results * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public static function searchByName($search, $mode = '') + public static function searchByName(string $search, string $mode = '', int $uid = 0) { if (empty($search)) { return []; @@ -2800,7 +2801,7 @@ class Contact NOT `failed` AND `uid` = ? AND (`addr` LIKE ? OR `name` LIKE ? OR `nick` LIKE ?) $extra_sql ORDER BY `nurl` DESC LIMIT 1000", - Protocol::DFRN, Protocol::ACTIVITYPUB, $ostatus, $diaspora, 0, $search, $search, $search + Protocol::DFRN, Protocol::ACTIVITYPUB, $ostatus, $diaspora, $uid, $search, $search, $search ); $contacts = DBA::toArray($results); diff --git a/src/Module/Api/Mastodon/Accounts/Followers.php b/src/Module/Api/Mastodon/Accounts/Followers.php index 739341a2fa..ab2ca68391 100644 --- a/src/Module/Api/Mastodon/Accounts/Followers.php +++ b/src/Module/Api/Mastodon/Accounts/Followers.php @@ -81,6 +81,10 @@ class Followers extends BaseApi } DBA::close($followers); + if (!empty($min_id)) { + array_reverse($accounts); + } + System::jsonExit($accounts); } } diff --git a/src/Module/Api/Mastodon/Accounts/Following.php b/src/Module/Api/Mastodon/Accounts/Following.php index 10d73e9071..414213dc44 100644 --- a/src/Module/Api/Mastodon/Accounts/Following.php +++ b/src/Module/Api/Mastodon/Accounts/Following.php @@ -81,6 +81,10 @@ class Following extends BaseApi } DBA::close($followers); + if (!empty($min_id)) { + array_reverse($accounts); + } + System::jsonExit($accounts); } } diff --git a/src/Module/Api/Mastodon/Accounts/Lists.php b/src/Module/Api/Mastodon/Accounts/Lists.php new file mode 100644 index 0000000000..95677dad6d --- /dev/null +++ b/src/Module/Api/Mastodon/Accounts/Lists.php @@ -0,0 +1,66 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon\Accounts; + +use Friendica\Core\System; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Model\Contact; +use Friendica\Module\BaseApi; + +/** + * @see https://docs.joinmastodon.org/methods/accounts/ + */ +class Lists extends BaseApi +{ + /** + * @param array $parameters + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function rawContent(array $parameters = []) + { + self::login(); + $uid = self::getCurrentUserID(); + + if (empty($parameters['id'])) { + DI::mstdnError()->RecordNotFound(); + } + + $id = $parameters['id']; + if (!DBA::exists('contact', ['id' => $id, 'uid' => 0])) { + DI::mstdnError()->RecordNotFound(); + } + + $lists = []; + + $cdata = Contact::getPublicAndUserContacID($id, $uid); + if (!empty($cdata['user'])) { + $groups = DBA::select('group_member', ['gid'], ['contact-id' => $cdata['user']]); + while ($group = DBA::fetch($groups)) { + $lists[] = DI::mstdnList()->create($group['gid']); + } + DBA::close($groups); + } + + System::jsonExit($lists); + } +} diff --git a/src/Module/Api/Mastodon/Accounts/Search.php b/src/Module/Api/Mastodon/Accounts/Search.php new file mode 100644 index 0000000000..17adb54851 --- /dev/null +++ b/src/Module/Api/Mastodon/Accounts/Search.php @@ -0,0 +1,98 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon\Accounts; + +use Friendica\Core\Search as CoreSearch; +use Friendica\Core\System; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Model\Contact; +use Friendica\Module\BaseApi; +use Friendica\Object\Search\ContactResult; +/** + * @see https://docs.joinmastodon.org/methods/accounts/ + */ +class Search extends BaseApi +{ + /** + * @param array $parameters + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function rawContent(array $parameters = []) + { + self::login(); + $uid = self::getCurrentUserID(); + + // What to search for + $q = (int)!isset($_REQUEST['q']) ? 0 : $_REQUEST['q']; + // Maximum number of results. Defaults to 40. + $limit = (int)!isset($_REQUEST['limit']) ? 40 : $_REQUEST['limit']; + // Attempt WebFinger lookup. Defaults to false. Use this when q is an exact address. + $resolve = (int)!isset($_REQUEST['resolve']) ? 0 : $_REQUEST['resolve']; + // Only who the user is following. Defaults to false. + $following = (int)!isset($_REQUEST['following']) ? 0 : $_REQUEST['following']; + + $accounts = []; + + if (!$following) { + if ((strrpos($q, '@') > 0) && $resolve) { + $results = CoreSearch::getContactsFromProbe($q); + } + + if (empty($results)) { + if (DI::config()->get('system', 'poco_local_search')) { + $results = CoreSearch::getContactsFromLocalDirectory($q, CoreSearch::TYPE_ALL, 0, $limit); + } elseif (!empty(DI::config()->get('system', 'directory'))) { + $results = CoreSearch::getContactsFromGlobalDirectory($q, CoreSearch::TYPE_ALL, 1); + } + } + + if (!empty($results)) { + $counter = 0; + foreach ($results->getResults() as $result) { + if (++$counter > $limit) { + continue; + } + if ($result instanceof ContactResult) { + $id = Contact::getIdForURL($result->getUrl(), 0, false); + $accounts[] = DI::mstdnAccount()->createFromContactId($id, $uid); + } + } + } + } else { + $contacts = Contact::searchByName($q, '', $uid); + $counter = 0; + foreach ($contacts as $contact) { + if (!in_array($contact['rel'], [Contact::SHARING, Contact::FRIEND])) { + continue; + } + if (++$counter > $limit) { + continue; + } + $accounts[] = DI::mstdnAccount()->createFromContactId($contact['id'], $uid); + } + DBA::close($contacts); + } + + System::jsonExit($accounts); + } +} diff --git a/src/Module/Api/Mastodon/Blocks.php b/src/Module/Api/Mastodon/Blocks.php index 7f968f64b6..af8e77f889 100644 --- a/src/Module/Api/Mastodon/Blocks.php +++ b/src/Module/Api/Mastodon/Blocks.php @@ -80,6 +80,10 @@ class Blocks extends BaseApi } DBA::close($followers); + if (!empty($min_id)) { + array_reverse($accounts); + } + System::jsonExit($accounts); } } diff --git a/src/Module/Api/Mastodon/Lists.php b/src/Module/Api/Mastodon/Lists.php new file mode 100644 index 0000000000..f65ef51395 --- /dev/null +++ b/src/Module/Api/Mastodon/Lists.php @@ -0,0 +1,76 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon; + +use Friendica\Core\System; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Module\BaseApi; + +/** + * @see https://docs.joinmastodon.org/methods/timelines/lists/ + */ +class Lists extends BaseApi +{ + public static function delete(array $parameters = []) + { + self::unsupported('delete'); + } + + public static function post(array $parameters = []) + { + self::unsupported('post'); + } + + public static function put(array $parameters = []) + { + self::unsupported('put'); + } + + /** + * @param array $parameters + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function rawContent(array $parameters = []) + { + self::login(); + $uid = self::getCurrentUserID(); + + if (empty($parameters['id'])) { + $lists = []; + + $groups = DBA::select('group', ['id'], ['uid' => $uid, 'deleted' => false]); + while ($group = DBA::fetch($groups)) { + $lists[] = DI::mstdnList()->create($group['id']); + } + DBA::close($groups); + } else { + $id = $parameters['id']; + if (!DBA::exists('group',['uid' => $uid, 'deleted' => false])) { + DI::mstdnError()->RecordNotFound(); + } + $lists = DI::mstdnList()->create($id); + } + + System::jsonExit($lists); + } +} diff --git a/src/Module/Api/Mastodon/Lists/Accounts.php b/src/Module/Api/Mastodon/Lists/Accounts.php new file mode 100644 index 0000000000..0b817cfc82 --- /dev/null +++ b/src/Module/Api/Mastodon/Lists/Accounts.php @@ -0,0 +1,102 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon\Lists; + +use Friendica\Core\System; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Module\BaseApi; + +/** + * @see https://docs.joinmastodon.org/methods/timelines/lists/ + * + * Currently the output will be unordered since we use public contact ids in the api and not user contact ids. + */ +class Accounts extends BaseApi +{ + public static function delete(array $parameters = []) + { + self::unsupported('delete'); + } + + public static function post(array $parameters = []) + { + self::unsupported('post'); + } + + /** + * @param array $parameters + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function rawContent(array $parameters = []) + { + self::login(); + $uid = self::getCurrentUserID(); + + if (empty($parameters['id'])) { + DI::mstdnError()->RecordNotFound(); + } + + $id = $parameters['id']; + if (!DBA::exists('group', ['id' => $id, 'uid' => $uid])) { + DI::mstdnError()->RecordNotFound(); + } + + // Return results older than this id + $max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id']; + // Return results newer than this id + $since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id']; + // Maximum number of results to return. Defaults to 20. + $limit = (int)!isset($_REQUEST['limit']) ? 40 : $_REQUEST['limit']; + + + $params = ['order' => ['contact-id' => true], 'limit' => $limit]; + + $condition = ['gid' => $id]; + + if (!empty($max_id)) { + $condition = DBA::mergeConditions($condition, ["`contact-id` < ?", $max_id]); + } + + if (!empty($since_id)) { + $condition = DBA::mergeConditions($condition, ["`contact-id` > ?", $since_id]); + } + + if (!empty($min_id)) { + $condition = DBA::mergeConditions($condition, ["`contact-id` > ?", $min_id]); + + $params['order'] = ['contact-id']; + } + + $members = DBA::select('group_member', ['contact-id'], $condition, $params); + while ($member = DBA::fetch($members)) { + $accounts[] = DI::mstdnAccount()->createFromContactId($member['contact-id'], $uid); + } + DBA::close($members); + + if (!empty($min_id)) { + array_reverse($accounts); + } + + System::jsonExit($accounts); + } +} diff --git a/src/Module/Api/Mastodon/Mutes.php b/src/Module/Api/Mastodon/Mutes.php index b5ec30de4d..cb981b33eb 100644 --- a/src/Module/Api/Mastodon/Mutes.php +++ b/src/Module/Api/Mastodon/Mutes.php @@ -80,6 +80,10 @@ class Mutes extends BaseApi } DBA::close($followers); + if (!empty($min_id)) { + array_reverse($accounts); + } + System::jsonExit($accounts); } } diff --git a/src/Module/Api/Mastodon/Preferences.php b/src/Module/Api/Mastodon/Preferences.php new file mode 100644 index 0000000000..fa27139058 --- /dev/null +++ b/src/Module/Api/Mastodon/Preferences.php @@ -0,0 +1,61 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon; + +use Friendica\Core\System; +use Friendica\DI; +use Friendica\Model\User; +use Friendica\Module\BaseApi; + +/** + * @see https://docs.joinmastodon.org/methods/accounts/preferences/ + */ +class Preferences extends BaseApi +{ + /** + * @param array $parameters + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function rawContent(array $parameters = []) + { + self::login(); + $uid = self::getCurrentUserID(); + + $user = User::getById($uid, ['language', 'allow_cid', 'allow_gid', 'deny_cid', 'deny_gid']); + if (!empty($user['allow_cid']) || !empty($user['allow_gid']) || !empty($user['deny_cid']) || !empty($user['deny_gid'])) { + $visibility = 'private'; + } elseif (DI::pConfig()->get($uid, 'system', 'unlisted')) { + $visibility = 'unlisted'; + } else { + $visibility = 'public'; + } + + $sensitive = false; + $language = $user['language']; + $media = DI::pConfig()->get($uid, 'nsfw', 'disable') ? 'show_all' : 'default'; + $spoilers = DI::pConfig()->get($uid, 'system', 'disable_cw'); + + $preferences = new \Friendica\Object\Api\Mastodon\Preferences($visibility, $sensitive, $language, $media, $spoilers); + + System::jsonExit($preferences); + } +} diff --git a/src/Object/Api/Mastodon/ListEntity.php b/src/Object/Api/Mastodon/ListEntity.php new file mode 100644 index 0000000000..b3407d28ed --- /dev/null +++ b/src/Object/Api/Mastodon/ListEntity.php @@ -0,0 +1,51 @@ +. + * + */ + +namespace Friendica\Object\Api\Mastodon; + +use Friendica\BaseDataTransferObject; + +/** + * Class ListEntity + * + * @see https://docs.joinmastodon.org/entities/list/ + */ +class ListEntity extends BaseDataTransferObject +{ + /** @var string */ + protected $id; + /** @var string */ + protected $title; + + /** + * Creates an list record + * + * @param int $id + * @param string $title + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public function __construct(int $id, string $title, string $policy) + { + $this->id = (string)$id; + $this->title = $title; + $this->replies_policy = $policy; + } +} diff --git a/src/Object/Api/Mastodon/Preferences.php b/src/Object/Api/Mastodon/Preferences.php new file mode 100644 index 0000000000..1b644beca8 --- /dev/null +++ b/src/Object/Api/Mastodon/Preferences.php @@ -0,0 +1,67 @@ +. + * + */ + +namespace Friendica\Object\Api\Mastodon; + +use Friendica\App\BaseURL; +use Friendica\BaseDataTransferObject; +use Friendica\Collection\Api\Mastodon\Fields; +use Friendica\Content\Text\BBCode; +use Friendica\Database\DBA; +use Friendica\Model\Contact; +use Friendica\Util\DateTimeFormat; + +/** + * Class Preferences + * + * @see https://docs.joinmastodon.org/entities/preferences/ + */ +class Preferences extends BaseDataTransferObject +{ +// /** @var string (Enumerable, oneOf) */ +// protected $posting_default_visibility; +// /** @var bool */ +// protected $posting_default_sensitive; +// /** @var string (ISO 639-1 language two-letter code), or null*/ +// protected $posting_default_language; +// /** @var string (Enumerable, oneOf) */ +// protected $reading_expand_media; +// /** @var bool */ +// protected $reading_expand_spoilers; + + /** + * Creates a preferences record. + * + * @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 != 0 + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public function __construct(string $visibility, bool $sensitive, string $language, string $media, bool $spoilers) + { + $this->{'posting:default:visibility'} = $visibility; + $this->{'posting:default:sensitive'} = $sensitive; + $this->{'posting:default:language'} = $language; + $this->{'reading:expand:media'} = $media; + $this->{'reading:expand:spoilers'} = $spoilers; + } +} diff --git a/static/routes.config.php b/static/routes.config.php index ddc755ec18..c786ca2fd2 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -62,7 +62,7 @@ return [ '/accounts/{id:\d+}/statuses' => [Module\Api\Mastodon\Accounts\Statuses::class, [R::GET ]], '/accounts/{id:\d+}/followers' => [Module\Api\Mastodon\Accounts\Followers::class, [R::GET ]], '/accounts/{id:\d+}/following' => [Module\Api\Mastodon\Accounts\Following::class, [R::GET ]], - '/accounts/{id:\d+}/lists' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo + '/accounts/{id:\d+}/lists' => [Module\Api\Mastodon\Accounts\Lists::class, [R::GET ]], '/accounts/{id:\d+}/identity_proofs' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented '/accounts/{id:\d+}/follow' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], '/accounts/{id:\d+}/unfollow' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], @@ -74,7 +74,7 @@ return [ '/accounts/{id:\d+}/unpin' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not implemented '/accounts/{id:\d+}/note' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], '/accounts/relationships' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo - '/accounts/search' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo + '/accounts/search' => [Module\Api\Mastodon\Accounts\Search::class, [R::GET ]], '/accounts/verify_credentials' => [Module\Api\Mastodon\Accounts\VerifyCredentials::class, [R::GET ]], '/accounts/update_credentials' => [Module\Api\Mastodon\Unimplemented::class, [R::PATCH ]], '/admin/accounts' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented @@ -108,9 +108,9 @@ return [ '/instance' => [Module\Api\Mastodon\Instance::class, [R::GET ]], '/instance/activity' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo '/instance/peers' => [Module\Api\Mastodon\Instance\Peers::class, [R::GET ]], - '/lists' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST]], // @todo - '/lists/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::PUT, R::DELETE]], // @todo - '/lists/{id:\d+}/accounts' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST, R::DELETE]], // @todo + '/lists' => [Module\Api\Mastodon\Lists::class, [R::GET, R::POST]], + '/lists/{id:\d+}' => [Module\Api\Mastodon\Lists::class, [R::GET, R::PUT, R::DELETE]], + '/lists/{id:\d+}/accounts' => [Module\Api\Mastodon\Lists\Accounts::class, [R::GET, R::POST, R::DELETE]], '/markers' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST]], // not implemented '/media' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], '/media/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::PUT]], // @todo @@ -121,7 +121,7 @@ return [ '/notifications/{id:\d+}/dismiss' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], '/polls/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented '/polls/{id:\d+}/votes' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not implemented - '/preferences' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo + '/preferences' => [Module\Api\Mastodon\Preferences::class, [R::GET ]], '/reports' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not implemented '/scheduled_statuses' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented '/scheduled_statuses/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::PUT, R::DELETE]], // not implemented From cc5d4db6a33bccf01bb96ffb0f1334381424f42b Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 11:50:05 +0000 Subject: [PATCH 02/15] Notifications are now supported as well --- doc/API-Mastodon.md | 2 + src/DI.php | 8 ++ src/Factory/Api/Mastodon/Notification.php | 89 ++++++++++++++++ src/Module/Api/Mastodon/Notifications.php | 124 ++++++++++++++++++++++ src/Object/Api/Mastodon/Notification.php | 78 ++++++++++++++ static/routes.config.php | 4 +- 6 files changed, 303 insertions(+), 2 deletions(-) create mode 100644 src/Factory/Api/Mastodon/Notification.php create mode 100644 src/Module/Api/Mastodon/Notifications.php create mode 100644 src/Object/Api/Mastodon/Notification.php diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 18338ac16c..2efa9a9ffa 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -48,6 +48,8 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - [`GET /api/v1/lists/:id`](https://docs.joinmastodon.org/methods/timelines/lists/) - [`GET /api/v1/lists/:id/accounts`](https://docs.joinmastodon.org/methods/timelines/lists/) - [`GET /api/v1/mutes`](https://docs.joinmastodon.org/methods/accounts/mutes/) +- [`GET /api/v1/notifications`](https://docs.joinmastodon.org/methods/notifications/) +- [`GET /api/v1/notifications/:id`](https://docs.joinmastodon.org/methods/notifications/) - [`GET /api/v1/preferences`](https://docs.joinmastodon.org/methods/accounts/preferences/) - [`GET /api/v1/statuses/:id`](https://docs.joinmastodon.org/methods/statuses/) - [`GET /api/v1/statuses/:id/context`](https://docs.joinmastodon.org/methods/statuses/) diff --git a/src/DI.php b/src/DI.php index 045425b887..987cc4fbed 100644 --- a/src/DI.php +++ b/src/DI.php @@ -311,6 +311,14 @@ abstract class DI return self::$dice->create(Factory\Api\Mastodon\Mention::class); } + /** + * @return Factory\Api\Mastodon\Notification + */ + public static function mstdnNotification() + { + return self::$dice->create(Factory\Api\Mastodon\Notification::class); + } + /** * @return Factory\Api\Mastodon\Tag */ diff --git a/src/Factory/Api/Mastodon/Notification.php b/src/Factory/Api/Mastodon/Notification.php new file mode 100644 index 0000000000..78b106c04e --- /dev/null +++ b/src/Factory/Api/Mastodon/Notification.php @@ -0,0 +1,89 @@ +. + * + */ + +namespace Friendica\Factory\Api\Mastodon; + +use Friendica\BaseFactory; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Model\Contact; +use Friendica\Model\Notification as ModelNotification; + +class Notification extends BaseFactory +{ + public function create(int $id) + { + $notification = DBA::selectFirst('notify', [], ['id' => $id]); + if (!DBA::isResult($notification)) { + return null; + } + + $cid = Contact::getIdForURL($notification['url'], 0, false); + if (empty($cid)) { + return null; + } + + /* + follow = Someone followed you + follow_request = Someone requested to follow you + mention = Someone mentioned you in their status + reblog = Someone boosted one of your statuses + favourite = Someone favourited one of your statuses + poll = A poll you have voted in or created has ended + status = Someone you enabled notifications for has posted a status + */ + + switch ($notification['type']) { + case ModelNotification\Type::INTRO: + $type = 'follow_request'; + break; + + case ModelNotification\Type::WALL: + case ModelNotification\Type::COMMENT: + case ModelNotification\Type::MAIL: + case ModelNotification\Type::TAG_SELF: + case ModelNotification\Type::POKE: + $type = 'mention'; + break; + + case ModelNotification\Type::SHARE: + $type = 'status'; + break; + + default: + return null; + } + + $account = DI::mstdnAccount()->createFromContactId($cid); + + if (!empty($notification['uri-id'])) { + try { + $status = DI::mstdnStatus()->createFromUriId($notification['uri-id'], $notification['uid']); + } catch (\Throwable $th) { + $status = null; + } + } else { + $status = null; + } + + return new \Friendica\Object\Api\Mastodon\Notification($id, $type, $notification['date'], $account, $status); + } +} diff --git a/src/Module/Api/Mastodon/Notifications.php b/src/Module/Api/Mastodon/Notifications.php new file mode 100644 index 0000000000..59bf3e0d39 --- /dev/null +++ b/src/Module/Api/Mastodon/Notifications.php @@ -0,0 +1,124 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon; + +use Friendica\Core\Logger; +use Friendica\Core\System; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Model\Contact; +use Friendica\Model\Notification; +use Friendica\Module\BaseApi; + +/** + * @see https://docs.joinmastodon.org/methods/accounts/mutes/ + */ +class Notifications extends BaseApi +{ + /** + * @param array $parameters + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function rawContent(array $parameters = []) + { + self::login(); + $uid = self::getCurrentUserID(); + + if (!empty($parameters['id'])) { + $id = $parameters['id']; + if (!DBA::exists('notify', ['id' => $id, 'uid' => $uid])) { + DI::mstdnError()->RecordNotFound(); + } + System::jsonExit(DI::mstdnNotification()->create($id)); + } + + // Return results older than this ID + $max_id = (int)!isset($_REQUEST['max_id']) ? 0 : $_REQUEST['max_id']; + + // Return results newer than this ID + $since_id = (int)!isset($_REQUEST['since_id']) ? 0 : $_REQUEST['since_id']; + + // Return results immediately newer than this ID + $min_id = (int)!isset($_REQUEST['min_id']) ? 0 : $_REQUEST['min_id']; + + // Maximum number of results to return (default 20) + $limit = (int)!isset($_REQUEST['limit']) ? 20 : $_REQUEST['limit']; + + // Array of types to exclude (follow, favourite, reblog, mention, poll, follow_request) + $exclude_types = !isset($_REQUEST['exclude_types']) ? [] : $_REQUEST['exclude_types']; + + // Return only notifications received from this account + $account_id = (int)!isset($_REQUEST['account_id']) ? 0 : $_REQUEST['account_id']; + + $params = ['order' => ['id' => true], 'limit' => $limit]; + + $condition = ['uid' => $uid, 'seen' => false, 'type' => []]; + + if (!empty($account_id)) { + $contact = Contact::getById($account_id, ['url']); + if (!empty($contact['url'])) { + $condition['url'] = $contact['url']; + } + } + + if (!in_array('follow_request', $exclude_types)) { + $condition['type'] = array_merge($condition['type'], [Notification\Type::INTRO]); + } + + if (!in_array('mention', $exclude_types)) { + $condition['type'] = array_merge($condition['type'], + [Notification\Type::WALL, Notification\Type::COMMENT, Notification\Type::MAIL, + Notification\Type::TAG_SELF, Notification\Type::POKE]); + } + + if (!in_array('status', $exclude_types)) { + $condition['type'] = array_merge($condition['type'], [Notification\Type::SHARE]); + } + + if (!empty($max_id)) { + $condition = DBA::mergeConditions($condition, ["`id` < ?", $max_id]); + } + + if (!empty($since_id)) { + $condition = DBA::mergeConditions($condition, ["`id` > ?", $since_id]); + } + + if (!empty($min_id)) { + $condition = DBA::mergeConditions($condition, ["`id` > ?", $min_id]); + + $params['order'] = ['id']; + } + + $notifications = []; + + $notify = DBA::select('notify', ['id'], $condition, $params); + while ($notification = DBA::fetch($notify)) { + $notifications[] = DI::mstdnNotification()->create($notification['id']); + } + + if (!empty($min_id)) { + array_reverse($notifications); + } + + System::jsonExit($notifications); + } +} diff --git a/src/Object/Api/Mastodon/Notification.php b/src/Object/Api/Mastodon/Notification.php new file mode 100644 index 0000000000..ee4930e3cd --- /dev/null +++ b/src/Object/Api/Mastodon/Notification.php @@ -0,0 +1,78 @@ +. + * + */ + +namespace Friendica\Object\Api\Mastodon; + +use Friendica\BaseDataTransferObject; +use Friendica\Util\DateTimeFormat; + +/** + * Class Notification + * + * @see https://docs.joinmastodon.org/entities/notification/ + */ +class Notification extends BaseDataTransferObject +{ + /** @var string */ + protected $id; + /** @var string (Enumerable oneOf) */ + protected $type; + /** @var string (Datetime) */ + protected $created_at; + /** @var Account */ + protected $account; + /** @var Status|null */ + protected $status = null; + + /** + * Creates a notification record + * + * @param array $item + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public function __construct(int $id, string $type, string $created_at, Account $account = null, Status $status = null) + { + $this->id = (string)$id; + $this->type = $type; + $this->created_at = DateTimeFormat::utc($created_at, DateTimeFormat::ATOM); + $this->account = $account->toArray(); + + if (!empty($status)) { + $this->status = $status->toArray(); + } + } + + /** + * Returns the current entity as an array + * + * @return array + */ + public function toArray(): array + { + $notification = parent::toArray(); + + if (!$notification['status']) { + unset($notification['status']); + } + + return $notification; + } +} diff --git a/static/routes.config.php b/static/routes.config.php index c786ca2fd2..e00a7f0e34 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -115,8 +115,8 @@ return [ '/media' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], '/media/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::PUT]], // @todo '/mutes' => [Module\Api\Mastodon\Mutes::class, [R::GET ]], - '/notifications' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo - '/notifications/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo + '/notifications' => [Module\Api\Mastodon\Notifications::class, [R::GET ]], + '/notifications/{id:\d+}' => [Module\Api\Mastodon\Notifications::class, [R::GET ]], '/notifications/clear' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], '/notifications/{id:\d+}/dismiss' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], '/polls/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented From f5c7f9670f25a51f9ffae527579b897237126cbb Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 11:54:34 +0000 Subject: [PATCH 03/15] Remove blanks --- include/enotify.php | 4 ++-- src/Factory/Api/Mastodon/Notification.php | 2 +- src/Model/Contact.php | 18 +++++++++--------- src/Module/Api/Mastodon/Accounts/Search.php | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/include/enotify.php b/include/enotify.php index 3aa38c1c44..1edad36ac0 100644 --- a/include/enotify.php +++ b/include/enotify.php @@ -41,7 +41,7 @@ use Friendica\Protocol\Activity; * type, event, otype, activity, verb, uid, cid, origin_cid, item, link, * source_name, source_mail, source_nick, source_link, source_photo, * show_in_notification_page - * + * * @return bool * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ @@ -273,7 +273,7 @@ function notification($params) $epreamble = $l10n->t('%1$s [url=%2$s]shared a post[/url] from %3$s.', '[url='.$params['source_link'].']'.$params['source_name'].'[/url]', $params['link'], '[url='.$params['origin_link'].']'.$params['origin_name'].'[/url]' - ); + ); } $sitelink = $l10n->t('Please visit %s to view and/or reply to the conversation.'); diff --git a/src/Factory/Api/Mastodon/Notification.php b/src/Factory/Api/Mastodon/Notification.php index 78b106c04e..d9588b450d 100644 --- a/src/Factory/Api/Mastodon/Notification.php +++ b/src/Factory/Api/Mastodon/Notification.php @@ -67,7 +67,7 @@ class Notification extends BaseFactory case ModelNotification\Type::SHARE: $type = 'status'; break; - + default: return null; } diff --git a/src/Model/Contact.php b/src/Model/Contact.php index 876369f304..f75cffadac 100644 --- a/src/Model/Contact.php +++ b/src/Model/Contact.php @@ -264,7 +264,7 @@ class Contact $condition = ['`alias` IN (?, ?, ?) AND `uid` = ? AND NOT `deleted`', $url, Strings::normaliseLink($url), $ssl_url, $uid]; $contact = DBA::selectFirst('contact', $fields, $condition, $options); } - + if (!DBA::isResult($contact)) { return []; } @@ -307,7 +307,7 @@ class Contact } $contact = self::getByURL($url, $update, $fields); - if (!empty($contact['id'])) { + if (!empty($contact['id'])) { $contact['cid'] = 0; $contact['zid'] = $contact['id']; } @@ -1274,7 +1274,7 @@ class Contact * * @param string $contact_url Contact URL * @param bool $thread_mode - * @param int $update Update mode + * @param int $update Update mode * @param int $parent Item parent ID for the update mode * @return string posts in HTML * @throws \Exception @@ -1289,7 +1289,7 @@ class Contact * * @param int $cid Contact ID * @param bool $thread_mode - * @param int $update Update mode + * @param int $update Update mode * @param int $parent Item parent ID for the update mode * @return string posts in HTML * @throws \Exception @@ -1347,7 +1347,7 @@ class Contact $o = ''; } - if ($thread_mode) { + if ($thread_mode) { $items = Post::toArray(Post::selectForUser(local_user(), ['uri-id', 'gravity', 'parent-uri-id', 'thr-parent-id', 'author-id'], $condition, $params)); $o .= conversation($a, $items, 'contacts', $update, false, 'commented', local_user()); @@ -1607,12 +1607,12 @@ class Contact $avatar['size'] = 48; $default = self::DEFAULT_AVATAR_MICRO; break; - + case Proxy::SIZE_THUMB: $avatar['size'] = 80; $default = self::DEFAULT_AVATAR_THUMB; break; - + case Proxy::SIZE_SMALL: default: $avatar['size'] = 300; @@ -1720,7 +1720,7 @@ class Contact $contact['thumb'] ?? '', $contact['micro'] ?? '', ]; - + foreach ($data as $image_uri) { $image_rid = Photo::ridFromURI($image_uri); if ($image_rid && !Photo::exists(['resource-id' => $image_rid, 'uid' => $uid])) { @@ -2027,7 +2027,7 @@ class Contact if (Contact\Relation::isDiscoverable($ret['url'])) { Worker::add(PRIORITY_LOW, 'ContactDiscovery', $ret['url']); } - + // Update the public contact if ($uid != 0) { $contact = self::getByURL($ret['url'], false, ['id']); diff --git a/src/Module/Api/Mastodon/Accounts/Search.php b/src/Module/Api/Mastodon/Accounts/Search.php index 17adb54851..0eda36e31e 100644 --- a/src/Module/Api/Mastodon/Accounts/Search.php +++ b/src/Module/Api/Mastodon/Accounts/Search.php @@ -74,7 +74,7 @@ class Search extends BaseApi } if ($result instanceof ContactResult) { $id = Contact::getIdForURL($result->getUrl(), 0, false); - $accounts[] = DI::mstdnAccount()->createFromContactId($id, $uid); + $accounts[] = DI::mstdnAccount()->createFromContactId($id, $uid); } } } From 7a576111c7b7f3f154fd141ea81c721a971a7f7a Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 11:56:34 +0000 Subject: [PATCH 04/15] Code styling --- src/Object/Api/Mastodon/Preferences.php | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/Object/Api/Mastodon/Preferences.php b/src/Object/Api/Mastodon/Preferences.php index 1b644beca8..d633c1128c 100644 --- a/src/Object/Api/Mastodon/Preferences.php +++ b/src/Object/Api/Mastodon/Preferences.php @@ -23,11 +23,6 @@ namespace Friendica\Object\Api\Mastodon; use Friendica\App\BaseURL; use Friendica\BaseDataTransferObject; -use Friendica\Collection\Api\Mastodon\Fields; -use Friendica\Content\Text\BBCode; -use Friendica\Database\DBA; -use Friendica\Model\Contact; -use Friendica\Util\DateTimeFormat; /** * Class Preferences @@ -59,9 +54,9 @@ class Preferences extends BaseDataTransferObject public function __construct(string $visibility, bool $sensitive, string $language, string $media, bool $spoilers) { $this->{'posting:default:visibility'} = $visibility; - $this->{'posting:default:sensitive'} = $sensitive; - $this->{'posting:default:language'} = $language; - $this->{'reading:expand:media'} = $media; - $this->{'reading:expand:spoilers'} = $spoilers; + $this->{'posting:default:sensitive'} = $sensitive; + $this->{'posting:default:language'} = $language; + $this->{'reading:expand:media'} = $media; + $this->{'reading:expand:spoilers'} = $spoilers; } } From 6e9dd7641ff7c9f5d4ac6a7afa3a24a0e1267f82 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 11:58:27 +0000 Subject: [PATCH 05/15] Some more code styling --- src/Module/Api/Mastodon/Accounts/Search.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/Module/Api/Mastodon/Accounts/Search.php b/src/Module/Api/Mastodon/Accounts/Search.php index 0eda36e31e..3509834fc6 100644 --- a/src/Module/Api/Mastodon/Accounts/Search.php +++ b/src/Module/Api/Mastodon/Accounts/Search.php @@ -74,12 +74,14 @@ class Search extends BaseApi } if ($result instanceof ContactResult) { $id = Contact::getIdForURL($result->getUrl(), 0, false); + $accounts[] = DI::mstdnAccount()->createFromContactId($id, $uid); } } } } else { $contacts = Contact::searchByName($q, '', $uid); + $counter = 0; foreach ($contacts as $contact) { if (!in_array($contact['rel'], [Contact::SHARING, Contact::FRIEND])) { From 66be56e0813483868da09f98ace50886e5975539 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 12:00:08 +0000 Subject: [PATCH 06/15] Some more formatting --- src/Module/Api/Mastodon/Accounts/Search.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Module/Api/Mastodon/Accounts/Search.php b/src/Module/Api/Mastodon/Accounts/Search.php index 3509834fc6..77906661d2 100644 --- a/src/Module/Api/Mastodon/Accounts/Search.php +++ b/src/Module/Api/Mastodon/Accounts/Search.php @@ -28,6 +28,7 @@ use Friendica\DI; use Friendica\Model\Contact; use Friendica\Module\BaseApi; use Friendica\Object\Search\ContactResult; + /** * @see https://docs.joinmastodon.org/methods/accounts/ */ From 6d3c82e54efc6eb64bad6b6006e4024a99ad18ac Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 12:01:44 +0000 Subject: [PATCH 07/15] Formatting --- src/Module/Api/Mastodon/Preferences.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Module/Api/Mastodon/Preferences.php b/src/Module/Api/Mastodon/Preferences.php index fa27139058..035357f07d 100644 --- a/src/Module/Api/Mastodon/Preferences.php +++ b/src/Module/Api/Mastodon/Preferences.php @@ -50,9 +50,9 @@ class Preferences extends BaseApi } $sensitive = false; - $language = $user['language']; - $media = DI::pConfig()->get($uid, 'nsfw', 'disable') ? 'show_all' : 'default'; - $spoilers = DI::pConfig()->get($uid, 'system', 'disable_cw'); + $language = $user['language']; + $media = DI::pConfig()->get($uid, 'nsfw', 'disable') ? 'show_all' : 'default'; + $spoilers = DI::pConfig()->get($uid, 'system', 'disable_cw'); $preferences = new \Friendica\Object\Api\Mastodon\Preferences($visibility, $sensitive, $language, $media, $spoilers); From 6cd7215d8761f2eef82c4d72dcb120db9ebc2f88 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 12:04:00 +0000 Subject: [PATCH 08/15] Formatting ... --- src/Module/Api/Mastodon/Notifications.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Module/Api/Mastodon/Notifications.php b/src/Module/Api/Mastodon/Notifications.php index 59bf3e0d39..91b58b603a 100644 --- a/src/Module/Api/Mastodon/Notifications.php +++ b/src/Module/Api/Mastodon/Notifications.php @@ -86,8 +86,7 @@ class Notifications extends BaseApi if (!in_array('mention', $exclude_types)) { $condition['type'] = array_merge($condition['type'], - [Notification\Type::WALL, Notification\Type::COMMENT, Notification\Type::MAIL, - Notification\Type::TAG_SELF, Notification\Type::POKE]); + [Notification\Type::WALL, Notification\Type::COMMENT, Notification\Type::MAIL, Notification\Type::TAG_SELF, Notification\Type::POKE]); } if (!in_array('status', $exclude_types)) { From 6ac74f9cc9dd13a974baffe4919544b02e789372 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 12:05:22 +0000 Subject: [PATCH 09/15] Unused use removed --- src/Module/Api/Mastodon/Notifications.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Module/Api/Mastodon/Notifications.php b/src/Module/Api/Mastodon/Notifications.php index 91b58b603a..8f1b1d2d97 100644 --- a/src/Module/Api/Mastodon/Notifications.php +++ b/src/Module/Api/Mastodon/Notifications.php @@ -21,7 +21,6 @@ namespace Friendica\Module\Api\Mastodon; -use Friendica\Core\Logger; use Friendica\Core\System; use Friendica\Database\DBA; use Friendica\DI; From 1c6e9155145edbaa56b9fc26d3539cc0f1b6c9e8 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 12:59:23 +0000 Subject: [PATCH 10/15] Media is now supported as well --- doc/API-Mastodon.md | 1 + src/Factory/Api/Mastodon/Attachment.php | 35 ++++++++++++++ src/Module/Api/Mastodon/Media.php | 62 +++++++++++++++++++++++++ static/routes.config.php | 8 ++-- 4 files changed, 102 insertions(+), 4 deletions(-) create mode 100644 src/Module/Api/Mastodon/Media.php diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 2efa9a9ffa..7f5b55797a 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -47,6 +47,7 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - [`GET /api/v1/lists`](https://docs.joinmastodon.org/methods/timelines/lists/) - [`GET /api/v1/lists/:id`](https://docs.joinmastodon.org/methods/timelines/lists/) - [`GET /api/v1/lists/:id/accounts`](https://docs.joinmastodon.org/methods/timelines/lists/) +- [`GET /api/v1/media/:id`](https://docs.joinmastodon.org/methods/statuses/media/) - [`GET /api/v1/mutes`](https://docs.joinmastodon.org/methods/accounts/mutes/) - [`GET /api/v1/notifications`](https://docs.joinmastodon.org/methods/notifications/) - [`GET /api/v1/notifications/:id`](https://docs.joinmastodon.org/methods/notifications/) diff --git a/src/Factory/Api/Mastodon/Attachment.php b/src/Factory/Api/Mastodon/Attachment.php index 893b4c9f95..7ac45f354e 100644 --- a/src/Factory/Api/Mastodon/Attachment.php +++ b/src/Factory/Api/Mastodon/Attachment.php @@ -23,9 +23,12 @@ namespace Friendica\Factory\Api\Mastodon; use Friendica\App\BaseURL; use Friendica\BaseFactory; +use Friendica\DI; +use Friendica\Model\Photo; use Friendica\Network\HTTPException; use Friendica\Model\Post; use Friendica\Repository\ProfileField; +use Friendica\Util\Images; use Friendica\Util\Proxy; use Psr\Log\LoggerInterface; @@ -93,4 +96,36 @@ class Attachment extends BaseFactory return $attachments; } + + /** + * @param int $id id of the photo + * @return array + * @throws HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public function createFromPhoto(int $id) + { + $photo = Photo::selectFirst(['resource-id', 'uid', 'id', 'title', 'type'], ['id' => $id]); + if (empty($photo)) { + return null; + } + + $attachment = ['id' => $photo['id'], 'description' => $photo['title']]; + + $phototypes = Images::supportedTypes(); + $ext = $phototypes[$photo['type']]; + + $url = DI::baseUrl() . '/photo/' . $photo['resource-id'] . '-0.' . $ext; + + $preview = Photo::selectFirst(['scale'], ["`resource-id` = ? AND `uid` = ? AND `scale` > ?", $photo['resource-id'], $photo['uid'], 0], ['order' => ['scale']]); + if (empty($scale)) { + $preview_url = DI::baseUrl() . '/photo/' . $photo['resource-id'] . '-' . $preview['scale'] . '.' . $ext; + } else { + $preview_url = ''; + } + + + $object = new \Friendica\Object\Api\Mastodon\Attachment($attachment, 'image', $url, $preview_url, ''); + return $object->toArray(); + } } diff --git a/src/Module/Api/Mastodon/Media.php b/src/Module/Api/Mastodon/Media.php new file mode 100644 index 0000000000..c5fb82a248 --- /dev/null +++ b/src/Module/Api/Mastodon/Media.php @@ -0,0 +1,62 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon; + +use Friendica\Core\System; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Model\Contact; +use Friendica\Model\Notification; +use Friendica\Model\Photo; +use Friendica\Module\BaseApi; + +/** + * @see https://docs.joinmastodon.org/methods/statuses/media/ + */ +class Media extends BaseApi +{ + public static function put(array $parameters = []) + { + self::unsupported('put'); + } + + /** + * @param array $parameters + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function rawContent(array $parameters = []) + { + self::login(); + $uid = self::getCurrentUserID(); + + if (empty($parameters['id'])) { + DI::mstdnError()->RecordNotFound(); + } + + $id = $parameters['id']; + if (!Photo::exists(['id' => $id, 'uid' => $uid])) { + DI::mstdnError()->RecordNotFound(); + } + + System::jsonExit(DI::mstdnAttachment()->createFromPhoto($id)); + } +} diff --git a/static/routes.config.php b/static/routes.config.php index e00a7f0e34..a21ff336da 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -90,9 +90,9 @@ return [ '/apps/verify_credentials' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], '/blocks' => [Module\Api\Mastodon\Blocks::class, [R::GET ]], '/bookmarks' => [Module\Api\Mastodon\Bookmarks::class, [R::GET ]], - '/conversations' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo - '/conversations/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::DELETE ]], - '/conversations/{id:\d+}/read' => [Module\Api\Mastodon\Unimplemented::class, [R::POST ]], + '/conversations' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented + '/conversations/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::DELETE ]], // not implemented + '/conversations/{id:\d+}/read' => [Module\Api\Mastodon\Unimplemented::class, [R::POST ]], // not implemented '/custom_emojis' => [Module\Api\Mastodon\CustomEmojis::class, [R::GET ]], '/domain_blocks' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST, R::DELETE]], // not implemented '/directory' => [Module\Api\Mastodon\Directory::class, [R::GET ]], @@ -113,7 +113,7 @@ return [ '/lists/{id:\d+}/accounts' => [Module\Api\Mastodon\Lists\Accounts::class, [R::GET, R::POST, R::DELETE]], '/markers' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST]], // not implemented '/media' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], - '/media/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::PUT]], // @todo + '/media/{id:\d+}' => [Module\Api\Mastodon\Media::class, [R::GET, R::PUT]], '/mutes' => [Module\Api\Mastodon\Mutes::class, [R::GET ]], '/notifications' => [Module\Api\Mastodon\Notifications::class, [R::GET ]], '/notifications/{id:\d+}' => [Module\Api\Mastodon\Notifications::class, [R::GET ]], From 591619e1d30cefff4d4fd71fadec5c11efef56f8 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 13:01:56 +0000 Subject: [PATCH 11/15] Unused use removed --- src/Module/Api/Mastodon/Media.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/Module/Api/Mastodon/Media.php b/src/Module/Api/Mastodon/Media.php index c5fb82a248..bc55610b26 100644 --- a/src/Module/Api/Mastodon/Media.php +++ b/src/Module/Api/Mastodon/Media.php @@ -22,10 +22,7 @@ namespace Friendica\Module\Api\Mastodon; use Friendica\Core\System; -use Friendica\Database\DBA; use Friendica\DI; -use Friendica\Model\Contact; -use Friendica\Model\Notification; use Friendica\Model\Photo; use Friendica\Module\BaseApi; From a696b7f427a3109b47b598624cfd3e015f7add0f Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 18:44:08 +0000 Subject: [PATCH 12/15] Renamed functions --- src/Factory/Api/Mastodon/ListEntity.php | 2 +- src/Factory/Api/Mastodon/Notification.php | 2 +- src/Module/Api/Mastodon/Accounts/Lists.php | 2 +- src/Module/Api/Mastodon/Lists.php | 4 ++-- src/Module/Api/Mastodon/Notifications.php | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Factory/Api/Mastodon/ListEntity.php b/src/Factory/Api/Mastodon/ListEntity.php index 57547e438a..a149b25af2 100644 --- a/src/Factory/Api/Mastodon/ListEntity.php +++ b/src/Factory/Api/Mastodon/ListEntity.php @@ -26,7 +26,7 @@ use Friendica\Database\DBA; class ListEntity extends BaseFactory { - public function create(int $id) + public function createFromGroupId(int $id) { $group = DBA::selectFirst('group', ['name'], ['id' => $id, 'deleted' => false]); return new \Friendica\Object\Api\Mastodon\ListEntity($id, $group['name'] ?? '', 'list'); diff --git a/src/Factory/Api/Mastodon/Notification.php b/src/Factory/Api/Mastodon/Notification.php index d9588b450d..db5e7d4898 100644 --- a/src/Factory/Api/Mastodon/Notification.php +++ b/src/Factory/Api/Mastodon/Notification.php @@ -29,7 +29,7 @@ use Friendica\Model\Notification as ModelNotification; class Notification extends BaseFactory { - public function create(int $id) + public function createFromNotifyId(int $id) { $notification = DBA::selectFirst('notify', [], ['id' => $id]); if (!DBA::isResult($notification)) { diff --git a/src/Module/Api/Mastodon/Accounts/Lists.php b/src/Module/Api/Mastodon/Accounts/Lists.php index 95677dad6d..b15124a45c 100644 --- a/src/Module/Api/Mastodon/Accounts/Lists.php +++ b/src/Module/Api/Mastodon/Accounts/Lists.php @@ -56,7 +56,7 @@ class Lists extends BaseApi if (!empty($cdata['user'])) { $groups = DBA::select('group_member', ['gid'], ['contact-id' => $cdata['user']]); while ($group = DBA::fetch($groups)) { - $lists[] = DI::mstdnList()->create($group['gid']); + $lists[] = DI::mstdnList()->createFromGroupId($group['gid']); } DBA::close($groups); } diff --git a/src/Module/Api/Mastodon/Lists.php b/src/Module/Api/Mastodon/Lists.php index f65ef51395..d9f9c6e5a8 100644 --- a/src/Module/Api/Mastodon/Lists.php +++ b/src/Module/Api/Mastodon/Lists.php @@ -60,7 +60,7 @@ class Lists extends BaseApi $groups = DBA::select('group', ['id'], ['uid' => $uid, 'deleted' => false]); while ($group = DBA::fetch($groups)) { - $lists[] = DI::mstdnList()->create($group['id']); + $lists[] = DI::mstdnList()->createFromGroupId($group['id']); } DBA::close($groups); } else { @@ -68,7 +68,7 @@ class Lists extends BaseApi if (!DBA::exists('group',['uid' => $uid, 'deleted' => false])) { DI::mstdnError()->RecordNotFound(); } - $lists = DI::mstdnList()->create($id); + $lists = DI::mstdnList()->createFromGroupId($id); } System::jsonExit($lists); diff --git a/src/Module/Api/Mastodon/Notifications.php b/src/Module/Api/Mastodon/Notifications.php index 8f1b1d2d97..a10ebf5d50 100644 --- a/src/Module/Api/Mastodon/Notifications.php +++ b/src/Module/Api/Mastodon/Notifications.php @@ -47,7 +47,7 @@ class Notifications extends BaseApi if (!DBA::exists('notify', ['id' => $id, 'uid' => $uid])) { DI::mstdnError()->RecordNotFound(); } - System::jsonExit(DI::mstdnNotification()->create($id)); + System::jsonExit(DI::mstdnNotification()->createFromNotifyId($id)); } // Return results older than this ID @@ -110,7 +110,7 @@ class Notifications extends BaseApi $notify = DBA::select('notify', ['id'], $condition, $params); while ($notification = DBA::fetch($notify)) { - $notifications[] = DI::mstdnNotification()->create($notification['id']); + $notifications[] = DI::mstdnNotification()->createFromNotifyId($notification['id']); } if (!empty($min_id)) { From 585d283ff9365d8c9db5c6ad4a023781404e9f37 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 22:23:21 +0000 Subject: [PATCH 13/15] Preparation for Oauth client registration --- database.sql | 17 +++++- src/DI.php | 8 +++ src/Factory/Api/Mastodon/Application.php | 49 +++++++++++++++++ src/Module/Api/Mastodon/Apps.php | 68 ++++++++++++++++++++++++ src/Object/Api/Mastodon/Application.php | 42 ++++++++++++++- static/dbstructure.config.php | 18 ++++++- static/routes.config.php | 2 +- 7 files changed, 199 insertions(+), 5 deletions(-) create mode 100644 src/Factory/Api/Mastodon/Application.php create mode 100644 src/Module/Api/Mastodon/Apps.php diff --git a/database.sql b/database.sql index 7a18fad5cb..666a48f667 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2021.06-dev (Siberian Iris) --- DB_UPDATE_VERSION 1414 +-- DB_UPDATE_VERSION 1415 -- ------------------------------------------ @@ -364,6 +364,21 @@ CREATE TABLE IF NOT EXISTS `apcontact` ( FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='ActivityPub compatible contacts - used in the ActivityPub implementation'; +-- +-- TABLE application +-- +CREATE TABLE IF NOT EXISTS `application` ( + `id` int unsigned NOT NULL auto_increment COMMENT 'generated index', + `client_id` varchar(64) NOT NULL COMMENT '', + `client_secret` varchar(64) NOT NULL COMMENT '', + `name` varchar(255) NOT NULL COMMENT '', + `redirect_uri` varchar(255) NOT NULL COMMENT '', + `website` varchar(255) COMMENT '', + `scopes` varchar(255) COMMENT '', + PRIMARY KEY(`id`), + UNIQUE INDEX `client_id` (`client_id`) +) DEFAULT COLLATE utf8mb4_general_ci COMMENT='OAuth application'; + -- -- TABLE attach -- diff --git a/src/DI.php b/src/DI.php index 987cc4fbed..37091a5ab8 100644 --- a/src/DI.php +++ b/src/DI.php @@ -239,6 +239,14 @@ abstract class DI return self::$dice->create(Factory\Api\Mastodon\Account::class); } + /** + * @return Factory\Api\Mastodon\Application + */ + public static function mstdnApplication() + { + return self::$dice->create(Factory\Api\Mastodon\Application::class); + } + /** * @return Factory\Api\Mastodon\Attachment */ diff --git a/src/Factory/Api/Mastodon/Application.php b/src/Factory/Api/Mastodon/Application.php new file mode 100644 index 0000000000..b45f4e9f4c --- /dev/null +++ b/src/Factory/Api/Mastodon/Application.php @@ -0,0 +1,49 @@ +. + * + */ + +namespace Friendica\Factory\Api\Mastodon; + +use Friendica\BaseFactory; +use Friendica\Database\DBA; + +class Application extends BaseFactory +{ + /** + * @param int $id Application ID + */ + public function createFromApplicationId(int $id) + { + $application = DBA::selectFirst('application', ['client_id', 'client_secret', 'id', 'name', 'redirect_uri', 'website'], ['id' => $id]); + if (!DBA::isResult($application)) { + return []; + } + + $object = new \Friendica\Object\Api\Mastodon\Application( + $application['name'], + $application['client_id'], + $application['client_secret'], + $application['id'], + $application['redirect_uri'], + $application['website']); + + return $object->toArray(); + } +} diff --git a/src/Module/Api/Mastodon/Apps.php b/src/Module/Api/Mastodon/Apps.php new file mode 100644 index 0000000000..36bf4ba868 --- /dev/null +++ b/src/Module/Api/Mastodon/Apps.php @@ -0,0 +1,68 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon; + +use Friendica\Core\System; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Module\BaseApi; + +/** + * Apps class to register new OAuth clients + */ +class Apps extends BaseApi +{ + /** + * @param array $parameters + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + public static function post(array $parameters = []) + { + $name = !isset($_REQUEST['client_name']) ? '' : $_REQUEST['client_name']; + $redirect = !isset($_REQUEST['redirect_uris']) ? '' : $_REQUEST['redirect_uris']; + $scopes = !isset($_REQUEST['scopes']) ? '' : $_REQUEST['scopes']; + $website = !isset($_REQUEST['website']) ? '' : $_REQUEST['website']; + + if (empty($name) || empty($redirect)) { + DI::mstdnError()->RecordNotFound(); + } + + $client_id = base64_encode(openssl_random_pseudo_bytes(32)); + $client_secret = bin2hex(random_bytes(32)); + + $fields = ['client_id' => $client_id, 'client_secret' => $client_secret, 'name' => $name, 'redirect_uri' => $redirect]; + + if (!empty($scopes)) { + $fields['scopes'] = $scopes; + } + + if (!empty($website)) { + $fields['website'] = $website; + } + + if (!DBA::insert('application', $fields)) { + DI::mstdnError()->RecordNotFound(); + } + + System::jsonExit(DI::mstdnApplication()->createFromApplicationId(DBA::lastInsertId())); + } +} diff --git a/src/Object/Api/Mastodon/Application.php b/src/Object/Api/Mastodon/Application.php index c8bb03a30d..1f31ca8ec7 100644 --- a/src/Object/Api/Mastodon/Application.php +++ b/src/Object/Api/Mastodon/Application.php @@ -30,8 +30,18 @@ use Friendica\BaseDataTransferObject; */ class Application extends BaseDataTransferObject { + /** @var string */ + protected $client_id; + /** @var string */ + protected $client_secret; + /** @var int */ + protected $id; /** @var string */ protected $name; + /** @var string */ + protected $redirect_uri; + /** @var string */ + protected $website; /** * Creates an application entry @@ -39,8 +49,36 @@ class Application extends BaseDataTransferObject * @param array $item * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function __construct(string $name) + public function __construct(string $name, string $client_id = null, string $client_secret = null, int $id = null, string $redirect_uri = null, string $website = null) { - $this->name = $name; + $this->client_id = $client_id; + $this->client_secret = $client_secret; + $this->id = $id; + $this->name = $name; + $this->redirect_uri = $redirect_uri; + $this->website = $website; + } + + /** + * Returns the current entity as an array + * + * @return array + */ + public function toArray(): array + { + $application = parent::toArray(); + + if (empty($application['id'])) { + unset($application['client_id']); + unset($application['client_secret']); + unset($application['id']); + unset($application['redirect_uri']); + } + + if (empty($application['website'])) { + unset($application['website']); + } + + return $application; } } diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 247552761f..052f73b9cf 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1414); + define('DB_UPDATE_VERSION', 1415); } return [ @@ -426,6 +426,22 @@ return [ "gsid" => ["gsid"] ] ], + "application" => [ + "comment" => "OAuth application", + "fields" => [ + "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "generated index"], + "client_id" => ["type" => "varchar(64)", "not null" => "1", "comment" => ""], + "client_secret" => ["type" => "varchar(64)", "not null" => "1", "comment" => ""], + "name" => ["type" => "varchar(255)", "not null" => "1", "comment" => ""], + "redirect_uri" => ["type" => "varchar(255)", "not null" => "1", "comment" => ""], + "website" => ["type" => "varchar(255)", "comment" => ""], + "scopes" => ["type" => "varchar(255)", "comment" => ""], + ], + "indexes" => [ + "PRIMARY" => ["id"], + "client_id" => ["UNIQUE", "client_id"] + ] + ], "attach" => [ "comment" => "file attachments", "fields" => [ diff --git a/static/routes.config.php b/static/routes.config.php index a21ff336da..5fbcffbd7c 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -86,7 +86,7 @@ return [ '/announcements' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented '/announcements/{id:\d+}/dismiss' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], // not implemented '/announcements/{id:\d+}/reactions/{name}' => [Module\Api\Mastodon\Unimplemented::class, [R::PUT, R::DELETE]], // not implemented - '/apps' => [Module\Api\Mastodon\Unimplemented::class, [ R::POST]], + '/apps' => [Module\Api\Mastodon\Apps::class, [ R::POST]], '/apps/verify_credentials' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], '/blocks' => [Module\Api\Mastodon\Blocks::class, [R::GET ]], '/bookmarks' => [Module\Api\Mastodon\Bookmarks::class, [R::GET ]], From 1ee1507f3b4c7385fda20e887f507d39c5123d75 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 22:26:32 +0000 Subject: [PATCH 14/15] Whitespaces ... --- src/Factory/Api/Mastodon/Application.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Factory/Api/Mastodon/Application.php b/src/Factory/Api/Mastodon/Application.php index b45f4e9f4c..3a184d8a5e 100644 --- a/src/Factory/Api/Mastodon/Application.php +++ b/src/Factory/Api/Mastodon/Application.php @@ -37,11 +37,11 @@ class Application extends BaseFactory } $object = new \Friendica\Object\Api\Mastodon\Application( - $application['name'], - $application['client_id'], - $application['client_secret'], - $application['id'], - $application['redirect_uri'], + $application['name'], + $application['client_id'], + $application['client_secret'], + $application['id'], + $application['redirect_uri'], $application['website']); return $object->toArray(); From 6644d117f6c121bfe7848d8c220b1301792565cf Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 9 May 2021 22:29:04 +0000 Subject: [PATCH 15/15] Indentions --- src/Module/Api/Mastodon/Apps.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Module/Api/Mastodon/Apps.php b/src/Module/Api/Mastodon/Apps.php index 36bf4ba868..a86d5cc993 100644 --- a/src/Module/Api/Mastodon/Apps.php +++ b/src/Module/Api/Mastodon/Apps.php @@ -37,16 +37,16 @@ class Apps extends BaseApi */ public static function post(array $parameters = []) { - $name = !isset($_REQUEST['client_name']) ? '' : $_REQUEST['client_name']; + $name = !isset($_REQUEST['client_name']) ? '' : $_REQUEST['client_name']; $redirect = !isset($_REQUEST['redirect_uris']) ? '' : $_REQUEST['redirect_uris']; - $scopes = !isset($_REQUEST['scopes']) ? '' : $_REQUEST['scopes']; - $website = !isset($_REQUEST['website']) ? '' : $_REQUEST['website']; + $scopes = !isset($_REQUEST['scopes']) ? '' : $_REQUEST['scopes']; + $website = !isset($_REQUEST['website']) ? '' : $_REQUEST['website']; if (empty($name) || empty($redirect)) { DI::mstdnError()->RecordNotFound(); } - $client_id = base64_encode(openssl_random_pseudo_bytes(32)); + $client_id = base64_encode(openssl_random_pseudo_bytes(32)); $client_secret = bin2hex(random_bytes(32)); $fields = ['client_id' => $client_id, 'client_secret' => $client_secret, 'name' => $name, 'redirect_uri' => $redirect];