From 0e2c2cd0e0fc8c2ed7a94ef7b4b22c98f562656d Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 27 Nov 2022 19:22:41 +0000 Subject: [PATCH 01/12] API: Support new tag endpoints --- .htaccess-dist | 2 +- src/Module/Api/Mastodon/Tags.php | 48 +++++++++++++++++++++++ src/Module/Api/Mastodon/Tags/Follow.php | 44 +++++++++++++++++++++ src/Module/Api/Mastodon/Tags/Unfollow.php | 44 +++++++++++++++++++++ static/routes.config.php | 6 ++- 5 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 src/Module/Api/Mastodon/Tags.php create mode 100644 src/Module/Api/Mastodon/Tags/Follow.php create mode 100644 src/Module/Api/Mastodon/Tags/Unfollow.php diff --git a/.htaccess-dist b/.htaccess-dist index 404137168..c5c1b5b71 100644 --- a/.htaccess-dist +++ b/.htaccess-dist @@ -51,6 +51,6 @@ AddType audio/ogg .oga RewriteCond %{REQUEST_FILENAME} !-f RewriteCond %{REQUEST_FILENAME} !-d - RewriteRule ^(.*)$ index.php?pagename=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA] + RewriteRule ^(.*)$ index.php?pagename=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA,B] diff --git a/src/Module/Api/Mastodon/Tags.php b/src/Module/Api/Mastodon/Tags.php new file mode 100644 index 000000000..97631b506 --- /dev/null +++ b/src/Module/Api/Mastodon/Tags.php @@ -0,0 +1,48 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon; + +use Friendica\App\Router; +use Friendica\Core\Logger; +use Friendica\DI; +use Friendica\Module\BaseApi; + +/** + * @see https://docs.joinmastodon.org/methods/tags/ + */ +class Tags extends BaseApi +{ + /** + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + protected function rawContent(array $request = []) + { + self::checkAllowedScope(self::SCOPE_READ); + $uid = self::getCurrentUserID(); + + if (empty($this->parameters['hashtag'])) { + DI::mstdnError()->UnprocessableEntity(); + } + + $this->response->unsupported(Router::GET, $request); + } +} diff --git a/src/Module/Api/Mastodon/Tags/Follow.php b/src/Module/Api/Mastodon/Tags/Follow.php new file mode 100644 index 000000000..9a813f725 --- /dev/null +++ b/src/Module/Api/Mastodon/Tags/Follow.php @@ -0,0 +1,44 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon\Tags; + +use Friendica\App\Router; +use Friendica\DI; +use Friendica\Module\BaseApi; + +/** + * @see https://docs.joinmastodon.org/methods/tags/#follow + */ +class Follow extends BaseApi +{ + protected function post(array $request = []) + { + self::checkAllowedScope(self::SCOPE_WRITE); + $uid = self::getCurrentUserID(); + + if (empty($this->parameters['hashtag'])) { + DI::mstdnError()->UnprocessableEntity(); + } + + $this->response->unsupported(Router::POST, $request); + } +} diff --git a/src/Module/Api/Mastodon/Tags/Unfollow.php b/src/Module/Api/Mastodon/Tags/Unfollow.php new file mode 100644 index 000000000..a20b824b8 --- /dev/null +++ b/src/Module/Api/Mastodon/Tags/Unfollow.php @@ -0,0 +1,44 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon\Tags; + +use Friendica\App\Router; +use Friendica\DI; +use Friendica\Module\BaseApi; + +/** + * @see https://docs.joinmastodon.org/methods/tags/#unfollow + */ +class Unfollow extends BaseApi +{ + protected function post(array $request = []) + { + self::checkAllowedScope(self::SCOPE_WRITE); + $uid = self::getCurrentUserID(); + + if (empty($this->parameters['hashtag'])) { + DI::mstdnError()->UnprocessableEntity(); + } + + $this->response->unsupported(Router::POST, $request); + } +} diff --git a/static/routes.config.php b/static/routes.config.php index ae5755711..522e5ff16 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -235,10 +235,10 @@ return [ '/featured_tags' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST]], // not supported '/featured_tags/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::DELETE ]], // not supported '/featured_tags/suggestions' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not supported - '/filters' => [Module\Api\Mastodon\Filters::class, [R::GET ]], // Dummy, not supported '/filters/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST, R::PUT, R::DELETE]], // not supported '/follow_requests' => [Module\Api\Mastodon\FollowRequests::class, [R::GET ]], '/follow_requests/{id:\d+}/{action}' => [Module\Api\Mastodon\FollowRequests::class, [ R::POST]], + '/followed_tags' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo '/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 ]], @@ -289,6 +289,9 @@ return [ '/streaming/user' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented '/streaming/user/notification' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented '/suggestions/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::DELETE ]], // not implemented + '/tags/{hashtag}' => [Module\Api\Mastodon\Tags::class, [R::GET ]], + '/tags/{hashtag}/follow' => [Module\Api\Mastodon\Tags\Follow::class, [ R::POST]], + '/tags/{hashtag}/unfollow' => [Module\Api\Mastodon\Tags\Unfollow::class, [ R::POST]], '/timelines/direct' => [Module\Api\Mastodon\Timelines\Direct::class, [R::GET ]], '/timelines/home' => [Module\Api\Mastodon\Timelines\Home::class, [R::GET ]], '/timelines/list/{id:\d+}' => [Module\Api\Mastodon\Timelines\ListTimeline::class, [R::GET ]], @@ -301,6 +304,7 @@ return [ ], '/v{version:\d+}' => [ '/admin/accounts' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not supported + '/filters' => [Module\Api\Mastodon\Filters::class, [R::GET ]], // Dummy, not supported '/media' => [Module\Api\Mastodon\Media::class, [ R::POST]], '/search' => [Module\Api\Mastodon\Search::class, [R::GET ]], '/suggestions' => [Module\Api\Mastodon\Suggestions::class, [R::GET ]], From 85f57e69faaca3645c596afd12e4b024194ae349 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Nov 2022 03:29:43 +0000 Subject: [PATCH 02/12] Issue 12191: We can now follow and unfollow tags via API --- src/Content/Widget/SavedSearches.php | 2 +- src/Module/Api/Mastodon/FollowedTags.php | 83 +++++++++++++++++++++++ src/Module/Api/Mastodon/Tags.php | 11 ++- src/Module/Api/Mastodon/Tags/Follow.php | 11 ++- src/Module/Api/Mastodon/Tags/Unfollow.php | 10 ++- src/Object/Api/Mastodon/Tag.php | 15 ++-- static/routes.config.php | 2 +- 7 files changed, 120 insertions(+), 14 deletions(-) create mode 100644 src/Module/Api/Mastodon/FollowedTags.php diff --git a/src/Content/Widget/SavedSearches.php b/src/Content/Widget/SavedSearches.php index 6b6202ba3..1bf9e76a2 100644 --- a/src/Content/Widget/SavedSearches.php +++ b/src/Content/Widget/SavedSearches.php @@ -37,7 +37,7 @@ class SavedSearches public static function getHTML(string $return_url, string $search = ''): string { $saved = []; - $saved_searches = DBA::select('search', ['id', 'term'], ['uid' => DI::userSession()->getLocalUserId()]); + $saved_searches = DBA::select('search', ['id', 'term'], ['uid' => DI::userSession()->getLocalUserId()], ['order' => ['term']]); while ($saved_search = DBA::fetch($saved_searches)) { $saved[] = [ 'id' => $saved_search['id'], diff --git a/src/Module/Api/Mastodon/FollowedTags.php b/src/Module/Api/Mastodon/FollowedTags.php new file mode 100644 index 000000000..6e95783c9 --- /dev/null +++ b/src/Module/Api/Mastodon/FollowedTags.php @@ -0,0 +1,83 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon; + +use Friendica\Core\System; +use Friendica\Database\DBA; +use Friendica\Module\BaseApi; + +/** + * @see https://docs.joinmastodon.org/methods/followed_tags/ + */ +class FollowedTags extends BaseApi +{ + protected function rawContent(array $request = []) + { + self::checkAllowedScope(self::SCOPE_READ); + $uid = self::getCurrentUserID(); + + $request = $this->getRequest([ + 'max_id' => 0, + 'since_id' => 0, + 'min_id' => 0, + 'limit' => 100, // Maximum number of results to return. Defaults to 100. Paginate using the HTTP Link header. + ], $request); + + $params = ['order' => ['id' => true], 'limit' => $request['limit']]; + + $condition = ["`uid` = ? AND `term` LIKE ?", $uid, '#%']; + + if (!empty($request['max_id'])) { + $condition = DBA::mergeConditions($condition, ["`id` < ?", $request['max_id']]); + } + + if (!empty($request['since_id'])) { + $condition = DBA::mergeConditions($condition, ["`id` > ?", $request['since_id']]); + } + + if (!empty($request['min_id'])) { + $condition = DBA::mergeConditions($condition, ["`id` > ?", $request['min_id']]); + + $params['order'] = ['id']; + } + + $return = []; + + $saved_searches = DBA::select('search', ['id', 'term'], $condition); + while ($saved_search = DBA::fetch($saved_searches)) { + self::setBoundaries($saved_search['id']); + $tag = ['name' => substr($saved_search['term'], 1)]; + + $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $tag, [], true); + $return[] = $hashtag->toArray(); + } + + DBA::close($saved_searches); + + if (!empty($request['min_id'])) { + $return = array_reverse($return); + } + + self::setLinkHeader(); + System::jsonExit($return); + } +} diff --git a/src/Module/Api/Mastodon/Tags.php b/src/Module/Api/Mastodon/Tags.php index 97631b506..442f0f657 100644 --- a/src/Module/Api/Mastodon/Tags.php +++ b/src/Module/Api/Mastodon/Tags.php @@ -21,8 +21,8 @@ namespace Friendica\Module\Api\Mastodon; -use Friendica\App\Router; -use Friendica\Core\Logger; +use Friendica\Core\System; +use Friendica\Database\DBA; use Friendica\DI; use Friendica\Module\BaseApi; @@ -43,6 +43,11 @@ class Tags extends BaseApi DI::mstdnError()->UnprocessableEntity(); } - $this->response->unsupported(Router::GET, $request); + $tag = ltrim($this->parameters['hashtag'], '#'); + $following = DBA::exists('search', ['uid' => $uid, 'term' => '#' . $tag]); + $term = ['term' => $tag]; + + $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $term, [], $following); + System::jsonExit($hashtag->toArray()); } } diff --git a/src/Module/Api/Mastodon/Tags/Follow.php b/src/Module/Api/Mastodon/Tags/Follow.php index 9a813f725..e2261f3fd 100644 --- a/src/Module/Api/Mastodon/Tags/Follow.php +++ b/src/Module/Api/Mastodon/Tags/Follow.php @@ -21,7 +21,8 @@ namespace Friendica\Module\Api\Mastodon\Tags; -use Friendica\App\Router; +use Friendica\Core\System; +use Friendica\Database\DBA; use Friendica\DI; use Friendica\Module\BaseApi; @@ -39,6 +40,12 @@ class Follow extends BaseApi DI::mstdnError()->UnprocessableEntity(); } - $this->response->unsupported(Router::POST, $request); + $fields = ['uid' => $uid, 'term' => '#' . $this->parameters['hashtag']]; + if (!DBA::exists('search', $fields)) { + DBA::insert('search', $fields); + } + + $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $fields, [], true); + System::jsonExit($hashtag->toArray()); } } diff --git a/src/Module/Api/Mastodon/Tags/Unfollow.php b/src/Module/Api/Mastodon/Tags/Unfollow.php index a20b824b8..465c1974b 100644 --- a/src/Module/Api/Mastodon/Tags/Unfollow.php +++ b/src/Module/Api/Mastodon/Tags/Unfollow.php @@ -21,7 +21,8 @@ namespace Friendica\Module\Api\Mastodon\Tags; -use Friendica\App\Router; +use Friendica\Core\System; +use Friendica\Database\DBA; use Friendica\DI; use Friendica\Module\BaseApi; @@ -39,6 +40,11 @@ class Unfollow extends BaseApi DI::mstdnError()->UnprocessableEntity(); } - $this->response->unsupported(Router::POST, $request); + $term = ['uid' => $uid, 'term' => '#' . $this->parameters['hashtag']]; + + DBA::delete('search', $term); + + $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $term, [], false); + System::jsonExit($hashtag->toArray()); } } diff --git a/src/Object/Api/Mastodon/Tag.php b/src/Object/Api/Mastodon/Tag.php index 340e30f70..e40b793e2 100644 --- a/src/Object/Api/Mastodon/Tag.php +++ b/src/Object/Api/Mastodon/Tag.php @@ -37,18 +37,23 @@ class Tag extends BaseDataTransferObject protected $url = null; /** @var array */ protected $history = []; + /** @var bool */ + protected $following = false; /** * Creates a hashtag record from an tag-view record. * * @param BaseURL $baseUrl - * @param array $tag tag-view record + * @param array $tag tag-view record + * @param array $history + * @param array $following "true" if the user is following this tag * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - public function __construct(BaseURL $baseUrl, array $tag, array $history = []) + public function __construct(BaseURL $baseUrl, array $tag, array $history = [], bool $following = false) { - $this->name = strtolower($tag['name']); - $this->url = $baseUrl . '/search?tag=' . urlencode($this->name); - $this->history = $history; + $this->name = strtolower($tag['name']); + $this->url = $baseUrl . '/search?tag=' . urlencode($this->name); + $this->history = $history; + $this->following = $following; } } diff --git a/static/routes.config.php b/static/routes.config.php index 07c2b4550..42333e65f 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -236,7 +236,7 @@ return [ '/filters/{id:\d+}' => [Module\Api\Mastodon\Unimplemented::class, [R::GET, R::POST, R::PUT, R::DELETE]], // not supported '/follow_requests' => [Module\Api\Mastodon\FollowRequests::class, [R::GET ]], '/follow_requests/{id:\d+}/{action}' => [Module\Api\Mastodon\FollowRequests::class, [ R::POST]], - '/followed_tags' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // @todo + '/followed_tags' => [Module\Api\Mastodon\FollowedTags::class, [R::GET ]], '/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 ]], From 4e6b6fd76b7b76fe577e3dfe19daf38a5a32aa90 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Mon, 28 Nov 2022 05:00:08 +0100 Subject: [PATCH 03/12] Update src/Module/Api/Mastodon/FollowedTags.php Co-authored-by: Hypolite Petovan --- src/Module/Api/Mastodon/FollowedTags.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module/Api/Mastodon/FollowedTags.php b/src/Module/Api/Mastodon/FollowedTags.php index 6e95783c9..eb5e32fdc 100644 --- a/src/Module/Api/Mastodon/FollowedTags.php +++ b/src/Module/Api/Mastodon/FollowedTags.php @@ -65,7 +65,7 @@ class FollowedTags extends BaseApi $saved_searches = DBA::select('search', ['id', 'term'], $condition); while ($saved_search = DBA::fetch($saved_searches)) { self::setBoundaries($saved_search['id']); - $tag = ['name' => substr($saved_search['term'], 1)]; + $tag = ['name' => ltrim($saved_search['term'], '#')]; $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $tag, [], true); $return[] = $hashtag->toArray(); From 934766c36406b1797741ae67b9b3840598f2cccb Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Nov 2022 04:04:27 +0000 Subject: [PATCH 04/12] Trim the tag --- src/Module/Api/Mastodon/Tags/Follow.php | 2 +- src/Module/Api/Mastodon/Tags/Unfollow.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Module/Api/Mastodon/Tags/Follow.php b/src/Module/Api/Mastodon/Tags/Follow.php index e2261f3fd..767245b60 100644 --- a/src/Module/Api/Mastodon/Tags/Follow.php +++ b/src/Module/Api/Mastodon/Tags/Follow.php @@ -40,7 +40,7 @@ class Follow extends BaseApi DI::mstdnError()->UnprocessableEntity(); } - $fields = ['uid' => $uid, 'term' => '#' . $this->parameters['hashtag']]; + $fields = ['uid' => $uid, 'term' => '#' . ltrim($this->parameters['hashtag'], '#')]; if (!DBA::exists('search', $fields)) { DBA::insert('search', $fields); } diff --git a/src/Module/Api/Mastodon/Tags/Unfollow.php b/src/Module/Api/Mastodon/Tags/Unfollow.php index 465c1974b..163b65003 100644 --- a/src/Module/Api/Mastodon/Tags/Unfollow.php +++ b/src/Module/Api/Mastodon/Tags/Unfollow.php @@ -40,7 +40,7 @@ class Unfollow extends BaseApi DI::mstdnError()->UnprocessableEntity(); } - $term = ['uid' => $uid, 'term' => '#' . $this->parameters['hashtag']]; + $term = ['uid' => $uid, 'term' => '#' . ltrim($this->parameters['hashtag'], '#')]; DBA::delete('search', $term); From f7167acc74db0ac31a9ccc83da6087424e261e18 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Nov 2022 04:07:25 +0000 Subject: [PATCH 05/12] Standards --- src/Module/Api/Mastodon/FollowedTags.php | 2 +- src/Module/Api/Mastodon/Tags.php | 2 +- src/Module/Api/Mastodon/Tags/Follow.php | 2 +- src/Module/Api/Mastodon/Tags/Unfollow.php | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Module/Api/Mastodon/FollowedTags.php b/src/Module/Api/Mastodon/FollowedTags.php index eb5e32fdc..bfa7db793 100644 --- a/src/Module/Api/Mastodon/FollowedTags.php +++ b/src/Module/Api/Mastodon/FollowedTags.php @@ -67,7 +67,7 @@ class FollowedTags extends BaseApi self::setBoundaries($saved_search['id']); $tag = ['name' => ltrim($saved_search['term'], '#')]; - $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $tag, [], true); + $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $tag, [], true); $return[] = $hashtag->toArray(); } diff --git a/src/Module/Api/Mastodon/Tags.php b/src/Module/Api/Mastodon/Tags.php index 442f0f657..d38899ac0 100644 --- a/src/Module/Api/Mastodon/Tags.php +++ b/src/Module/Api/Mastodon/Tags.php @@ -47,7 +47,7 @@ class Tags extends BaseApi $following = DBA::exists('search', ['uid' => $uid, 'term' => '#' . $tag]); $term = ['term' => $tag]; - $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $term, [], $following); + $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $term, [], $following); System::jsonExit($hashtag->toArray()); } } diff --git a/src/Module/Api/Mastodon/Tags/Follow.php b/src/Module/Api/Mastodon/Tags/Follow.php index 767245b60..22f8fa3f2 100644 --- a/src/Module/Api/Mastodon/Tags/Follow.php +++ b/src/Module/Api/Mastodon/Tags/Follow.php @@ -45,7 +45,7 @@ class Follow extends BaseApi DBA::insert('search', $fields); } - $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $fields, [], true); + $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $fields, [], true); System::jsonExit($hashtag->toArray()); } } diff --git a/src/Module/Api/Mastodon/Tags/Unfollow.php b/src/Module/Api/Mastodon/Tags/Unfollow.php index 163b65003..f3fbad2e5 100644 --- a/src/Module/Api/Mastodon/Tags/Unfollow.php +++ b/src/Module/Api/Mastodon/Tags/Unfollow.php @@ -44,7 +44,7 @@ class Unfollow extends BaseApi DBA::delete('search', $term); - $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $term, [], false); + $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $term, [], false); System::jsonExit($hashtag->toArray()); } } From a7639a2341bcd181d8730b220cab0f9ec18f965e Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Nov 2022 06:46:18 +0000 Subject: [PATCH 06/12] Updated documentation --- doc/API-Mastodon.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 63c487caf..6c6a1691a 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -73,7 +73,7 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - `:id` is a follow request ID, not a regular account id - Returns a [Relationship](https://docs.joinmastodon.org/entities/relationship) object. - +- [`GET /api/v1/followed_tags'](https://docs.joinmastodon.org/methods/followed_tags/) - [`GET /api/v1/instance`](https://docs.joinmastodon.org/methods/instance#fetch-instance) - `GET /api/v1/instance/rules` Undocumented, returns Terms of Service - [`GET /api/v1/instance/peers`](https://docs.joinmastodon.org/methods/instance#list-of-connected-domains) @@ -126,6 +126,9 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - [`GET /api/v1/statuses/:id/source`](https://docs.joinmastodon.org/methods/statuses/#source) - [`GET /api/v1/statuses/:id/card`](https://docs.joinmastodon.org/methods/statuses/#card) - [`GET /api/v1/suggestions`](https://docs.joinmastodon.org/methods/accounts/suggestions/) +- [`GET /api/v1/tags/:id'](https://docs.joinmastodon.org/methods/tags/#get) +- [`GET /api/v1/tags/:id/follow'](https://docs.joinmastodon.org/methods/tags/#follow) +- [`GET /api/v1/tags/:id/unfollow'](https://docs.joinmastodon.org/methods/tags/#unfollow) - [`GET /api/v1/timelines/direct`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/timelines/home`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/timelines/list/:id`](https://docs.joinmastodon.org/methods/timelines/) From 73ba7d800540f6106f1f2bb344e3f33a3bf12714 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Nov 2022 06:52:55 +0000 Subject: [PATCH 07/12] Coding styles --- src/Module/Api/Mastodon/FollowedTags.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module/Api/Mastodon/FollowedTags.php b/src/Module/Api/Mastodon/FollowedTags.php index bfa7db793..eb5e32fdc 100644 --- a/src/Module/Api/Mastodon/FollowedTags.php +++ b/src/Module/Api/Mastodon/FollowedTags.php @@ -67,7 +67,7 @@ class FollowedTags extends BaseApi self::setBoundaries($saved_search['id']); $tag = ['name' => ltrim($saved_search['term'], '#')]; - $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $tag, [], true); + $hashtag = new \Friendica\Object\Api\Mastodon\Tag($this->baseUrl, $tag, [], true); $return[] = $hashtag->toArray(); } From 6b36bd893bd8b1b44d578f19acbaa4d4bb670c36 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Nov 2022 07:08:01 +0000 Subject: [PATCH 08/12] API: Trending tags are added --- doc/API-Mastodon.md | 1 + static/routes.config.php | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index 6c6a1691a..d77218f67 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -135,6 +135,7 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - [`GET /api/v1/timelines/public`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/timelines/tag/:hashtag`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/trends`](https://docs.joinmastodon.org/methods/instance/trends/) +- [`GET /api/v1/trends/tags`](https://docs.joinmastodon.org/methods/trends/#tags) - [`GET /api/v2/search`](https://docs.joinmastodon.org/methods/search/) diff --git a/static/routes.config.php b/static/routes.config.php index 42333e65f..9b2d3d936 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -298,7 +298,7 @@ return [ '/trends' => [Module\Api\Mastodon\Trends::class, [R::GET ]], '/trends/links' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented '/trends/statuses' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented - '/trends/tags' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented + '/trends/tags' => [Module\Api\Mastodon\Trends::class, [R::GET ]], ], '/v{version:\d+}' => [ '/admin/accounts' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not supported From c00004f6ea05646310904c97c1289cc1c5ac1d2d Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Nov 2022 09:30:41 +0000 Subject: [PATCH 09/12] API: Tredning statuses added --- database.sql | 3 +- doc/API-Mastodon.md | 9 ++- src/Model/Post.php | 15 +++++ src/Module/Api/Mastodon/Trends/Statuses.php | 57 +++++++++++++++++++ .../Mastodon/{Trends.php => Trends/Tags.php} | 4 +- static/dbstructure.config.php | 2 +- static/dbview.config.php | 1 + static/routes.config.php | 6 +- 8 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 src/Module/Api/Mastodon/Trends/Statuses.php rename src/Module/Api/Mastodon/{Trends.php => Trends/Tags.php} (95%) diff --git a/database.sql b/database.sql index 8fa20be52..cccb233d3 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2022.12-dev (Giant Rhubarb) --- DB_UPDATE_VERSION 1491 +-- DB_UPDATE_VERSION 1492 -- ------------------------------------------ @@ -2437,6 +2437,7 @@ CREATE VIEW `post-thread-view` AS SELECT `post-question`.`end-time` AS `question-end-time`, 0 AS `has-categories`, EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post-thread`.`uri-id`) AS `has-media`, + (SELECT COUNT(*) FROM `post` WHERE `parent-uri-id` = `post-thread`.`uri-id` AND `gravity` = 6) AS `total-comments`, `diaspora-interaction`.`interaction` AS `signed_text`, `parent-item-uri`.`guid` AS `parent-guid`, `parent-post`.`network` AS `parent-network`, diff --git a/doc/API-Mastodon.md b/doc/API-Mastodon.md index d77218f67..2850f0ebf 100644 --- a/doc/API-Mastodon.md +++ b/doc/API-Mastodon.md @@ -126,15 +126,16 @@ These endpoints use the [Mastodon API entities](https://docs.joinmastodon.org/en - [`GET /api/v1/statuses/:id/source`](https://docs.joinmastodon.org/methods/statuses/#source) - [`GET /api/v1/statuses/:id/card`](https://docs.joinmastodon.org/methods/statuses/#card) - [`GET /api/v1/suggestions`](https://docs.joinmastodon.org/methods/accounts/suggestions/) -- [`GET /api/v1/tags/:id'](https://docs.joinmastodon.org/methods/tags/#get) -- [`GET /api/v1/tags/:id/follow'](https://docs.joinmastodon.org/methods/tags/#follow) -- [`GET /api/v1/tags/:id/unfollow'](https://docs.joinmastodon.org/methods/tags/#unfollow) +- [`GET /api/v1/tags/:id`](https://docs.joinmastodon.org/methods/tags/#get) +- [`GET /api/v1/tags/:id/follow`](https://docs.joinmastodon.org/methods/tags/#follow) +- [`GET /api/v1/tags/:id/unfollow`](https://docs.joinmastodon.org/methods/tags/#unfollow) - [`GET /api/v1/timelines/direct`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/timelines/home`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/timelines/list/:id`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/timelines/public`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/timelines/tag/:hashtag`](https://docs.joinmastodon.org/methods/timelines/) - [`GET /api/v1/trends`](https://docs.joinmastodon.org/methods/instance/trends/) +- [`GET /api/v1/trends/statuses`](https://docs.joinmastodon.org/methods/trends/#statuses) - [`GET /api/v1/trends/tags`](https://docs.joinmastodon.org/methods/trends/#tags) - [`GET /api/v2/search`](https://docs.joinmastodon.org/methods/search/) @@ -147,8 +148,6 @@ These emdpoints are planned to be implemented somewhere in the future. - [`GET /api/v1/accounts/familiar_followers`](https://github.com/mastodon/mastodon/pull/17700) - [`GET /api/v1/accounts/lookup`](https://github.com/mastodon/mastodon/pull/15740) - [`GET /api/v1/trends/links`](https://github.com/mastodon/mastodon/pull/16917) -- [`GET /api/v1/trends/statuses`](https://github.com/mastodon/mastodon/pull/17431) -- [`GET /api/v1/trends/tags`](https://github.com/mastodon/mastodon/pull/16917) - [`POST /api/v1/polls/:id/votes`](https://docs.joinmastodon.org/methods/statuses/polls/) - [`GET /api/v1/featured_tags`](https://docs.joinmastodon.org/methods/accounts/featured_tags/) - [`POST /api/v1/featured_tags`](https://docs.joinmastodon.org/methods/accounts/featured_tags/) diff --git a/src/Model/Post.php b/src/Model/Post.php index 11e5c2c98..2baa76b5b 100644 --- a/src/Model/Post.php +++ b/src/Model/Post.php @@ -374,6 +374,21 @@ class Post return self::selectView('post-thread-user-view', $selected, $condition, $params); } + /** + * Select rows from the post-thread-view view + * + * @param array $selected Array of selected fields, empty for all + * @param array $condition Array of fields for condition + * @param array $params Array of several parameters + * + * @return boolean|object + * @throws \Exception + */ + public static function selectPostThread(array $selected = [], array $condition = [], array $params = []) + { + return self::selectView('post-thread-view', $selected, $condition, $params); + } + /** * Select rows from the given view for a given user * diff --git a/src/Module/Api/Mastodon/Trends/Statuses.php b/src/Module/Api/Mastodon/Trends/Statuses.php new file mode 100644 index 000000000..70b23f5cc --- /dev/null +++ b/src/Module/Api/Mastodon/Trends/Statuses.php @@ -0,0 +1,57 @@ +. + * + */ + +namespace Friendica\Module\Api\Mastodon\Trends; + +use Friendica\Core\Protocol; +use Friendica\Core\System; +use Friendica\Database\DBA; +use Friendica\DI; +use Friendica\Model\Post; +use Friendica\Module\BaseApi; +use Friendica\Util\DateTimeFormat; + +/** + * @see https://docs.joinmastodon.org/methods/trends/#statuses + */ +class Statuses extends BaseApi +{ + /** + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + protected function rawContent(array $request = []) + { + $request = $this->getRequest([ + 'limit' => 10, // Maximum number of results to return. Defaults to 10. + ], $request); + + $trending = []; + $condition = ["NOT `private` AND `commented` > ?", DateTimeFormat::utc('now -1 day')]; + $condition = DBA::mergeConditions($condition, ['network' => Protocol::FEDERATED]); + $statuses = Post::selectPostThread(['uri-id'], $condition, ['limit' => $request['limit'], 'order' => ['total-comments' => true]]); + while ($status = Post::fetch($statuses)) { + $trending[] = DI::mstdnStatus()->createFromUriId($status['uri-id']); + } + DBA::close($statuses); + + System::jsonExit($trending); + } +} diff --git a/src/Module/Api/Mastodon/Trends.php b/src/Module/Api/Mastodon/Trends/Tags.php similarity index 95% rename from src/Module/Api/Mastodon/Trends.php rename to src/Module/Api/Mastodon/Trends/Tags.php index c42a27641..2014b7a38 100644 --- a/src/Module/Api/Mastodon/Trends.php +++ b/src/Module/Api/Mastodon/Trends/Tags.php @@ -19,7 +19,7 @@ * */ -namespace Friendica\Module\Api\Mastodon; +namespace Friendica\Module\Api\Mastodon\Trends; use Friendica\Core\System; use Friendica\DI; @@ -29,7 +29,7 @@ use Friendica\Module\BaseApi; /** * @see https://docs.joinmastodon.org/methods/instance/trends/ */ -class Trends extends BaseApi +class Tags extends BaseApi { /** * @throws \Friendica\Network\HTTPException\InternalServerErrorException diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index 942f2237d..2f613f974 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', 1491); + define('DB_UPDATE_VERSION', 1492); } return [ diff --git a/static/dbview.config.php b/static/dbview.config.php index 2452dc341..6c4f00a98 100644 --- a/static/dbview.config.php +++ b/static/dbview.config.php @@ -664,6 +664,7 @@ "question-end-time" => ["post-question", "end-time"], "has-categories" => "0", "has-media" => "EXISTS(SELECT `id` FROM `post-media` WHERE `post-media`.`uri-id` = `post-thread`.`uri-id`)", + "total-comments" => "(SELECT COUNT(*) FROM `post` WHERE `parent-uri-id` = `post-thread`.`uri-id` AND `gravity` = 6)", "signed_text" => ["diaspora-interaction", "interaction"], "parent-guid" => ["parent-item-uri", "guid"], "parent-network" => ["parent-post", "network"], diff --git a/static/routes.config.php b/static/routes.config.php index 9b2d3d936..81a9d2e2f 100644 --- a/static/routes.config.php +++ b/static/routes.config.php @@ -295,10 +295,10 @@ return [ '/timelines/list/{id:\d+}' => [Module\Api\Mastodon\Timelines\ListTimeline::class, [R::GET ]], '/timelines/public' => [Module\Api\Mastodon\Timelines\PublicTimeline::class, [R::GET ]], '/timelines/tag/{hashtag}' => [Module\Api\Mastodon\Timelines\Tag::class, [R::GET ]], - '/trends' => [Module\Api\Mastodon\Trends::class, [R::GET ]], + '/trends' => [Module\Api\Mastodon\Trends\Tags::class, [R::GET ]], '/trends/links' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented - '/trends/statuses' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not implemented - '/trends/tags' => [Module\Api\Mastodon\Trends::class, [R::GET ]], + '/trends/statuses' => [Module\Api\Mastodon\Trends\Statuses::class, [R::GET ]], + '/trends/tags' => [Module\Api\Mastodon\Trends\Tags::class, [R::GET ]], ], '/v{version:\d+}' => [ '/admin/accounts' => [Module\Api\Mastodon\Unimplemented::class, [R::GET ]], // not supported From 0e160040d49fdd82935a2d48530cdec552212b81 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Nov 2022 10:54:26 +0000 Subject: [PATCH 10/12] Code standards --- src/Module/Api/Mastodon/Trends/Statuses.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Module/Api/Mastodon/Trends/Statuses.php b/src/Module/Api/Mastodon/Trends/Statuses.php index 70b23f5cc..cb69be5ed 100644 --- a/src/Module/Api/Mastodon/Trends/Statuses.php +++ b/src/Module/Api/Mastodon/Trends/Statuses.php @@ -43,9 +43,10 @@ class Statuses extends BaseApi 'limit' => 10, // Maximum number of results to return. Defaults to 10. ], $request); - $trending = []; $condition = ["NOT `private` AND `commented` > ?", DateTimeFormat::utc('now -1 day')]; $condition = DBA::mergeConditions($condition, ['network' => Protocol::FEDERATED]); + + $trending = []; $statuses = Post::selectPostThread(['uri-id'], $condition, ['limit' => $request['limit'], 'order' => ['total-comments' => true]]); while ($status = Post::fetch($statuses)) { $trending[] = DI::mstdnStatus()->createFromUriId($status['uri-id']); From bded1c3989854bbf8a23fd39592cd42161813165 Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Nov 2022 14:51:37 +0000 Subject: [PATCH 11/12] Only use items that had originally been posted in the last week --- src/Module/Api/Mastodon/Trends/Statuses.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Module/Api/Mastodon/Trends/Statuses.php b/src/Module/Api/Mastodon/Trends/Statuses.php index cb69be5ed..8974403d7 100644 --- a/src/Module/Api/Mastodon/Trends/Statuses.php +++ b/src/Module/Api/Mastodon/Trends/Statuses.php @@ -43,7 +43,7 @@ class Statuses extends BaseApi 'limit' => 10, // Maximum number of results to return. Defaults to 10. ], $request); - $condition = ["NOT `private` AND `commented` > ?", DateTimeFormat::utc('now -1 day')]; + $condition = ["NOT `private` AND `commented` > ? AND `created` > ?", DateTimeFormat::utc('now -1 day'), DateTimeFormat::utc('now -1 week')]; $condition = DBA::mergeConditions($condition, ['network' => Protocol::FEDERATED]); $trending = []; From e391328cbf7edf05b6d40fd15c538f2f9260346a Mon Sep 17 00:00:00 2001 From: Michael Date: Mon, 28 Nov 2022 19:31:04 +0000 Subject: [PATCH 12/12] Added changelog entry --- CHANGELOG | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG b/CHANGELOG index b2e549610..b9a1dd966 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,6 @@ Version 2022.12 (unreleased) Friendica Core + The rewrite rule in .htaccess-dist has been changed. The change has to be applied manually to the existing .htaccess Friendica Addons BREAKING: The functions from the boot.php file have been moved into better fitting classes