diff --git a/src/Module/Api/Mastodon/Accounts/Followers.php b/src/Module/Api/Mastodon/Accounts/Followers.php index 666cafc42..c495d6ca1 100644 --- a/src/Module/Api/Mastodon/Accounts/Followers.php +++ b/src/Module/Api/Mastodon/Accounts/Followers.php @@ -77,6 +77,7 @@ class Followers extends BaseApi $followers = DBA::select('contact-relation', ['relation-cid'], $condition, $parameters); while ($follower = DBA::fetch($followers)) { + self::setBoundaries($follower['relation-cid']); $accounts[] = DI::mstdnAccount()->createFromContactId($follower['relation-cid'], $uid); } DBA::close($followers); @@ -85,6 +86,7 @@ class Followers extends BaseApi array_reverse($accounts); } + self::setLinkHeader(); System::jsonExit($accounts); } } diff --git a/src/Module/Api/Mastodon/Accounts/Following.php b/src/Module/Api/Mastodon/Accounts/Following.php index c967715df..b6a8f7f75 100644 --- a/src/Module/Api/Mastodon/Accounts/Following.php +++ b/src/Module/Api/Mastodon/Accounts/Following.php @@ -77,6 +77,7 @@ class Following extends BaseApi $followers = DBA::select('contact-relation', ['cid'], $condition, $parameters); while ($follower = DBA::fetch($followers)) { + self::setBoundaries($follower['cid']); $accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid); } DBA::close($followers); @@ -85,6 +86,7 @@ class Following extends BaseApi array_reverse($accounts); } + self::setLinkHeader(); System::jsonExit($accounts); } } diff --git a/src/Module/Api/Mastodon/Accounts/Statuses.php b/src/Module/Api/Mastodon/Accounts/Statuses.php index 3b281392b..0e7a7fb33 100644 --- a/src/Module/Api/Mastodon/Accounts/Statuses.php +++ b/src/Module/Api/Mastodon/Accounts/Statuses.php @@ -108,6 +108,7 @@ class Statuses extends BaseApi $statuses = []; while ($item = Post::fetch($items)) { + self::setBoundaries($item['uri-id']); $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid); } DBA::close($items); @@ -116,6 +117,7 @@ class Statuses extends BaseApi array_reverse($statuses); } + self::setLinkHeader(); System::jsonExit($statuses); } } diff --git a/src/Module/Api/Mastodon/Blocks.php b/src/Module/Api/Mastodon/Blocks.php index d92b6059d..37ab61af5 100644 --- a/src/Module/Api/Mastodon/Blocks.php +++ b/src/Module/Api/Mastodon/Blocks.php @@ -77,6 +77,7 @@ class Blocks extends BaseApi $followers = DBA::select('user-contact', ['cid'], $condition, $parameters); while ($follower = DBA::fetch($followers)) { + self::setBoundaries($follower['cid']); $accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid); } DBA::close($followers); @@ -85,6 +86,7 @@ class Blocks extends BaseApi array_reverse($accounts); } + self::setLinkHeader(); System::jsonExit($accounts); } } diff --git a/src/Module/Api/Mastodon/Bookmarks.php b/src/Module/Api/Mastodon/Bookmarks.php index 7e298d120..a7141e3dc 100644 --- a/src/Module/Api/Mastodon/Bookmarks.php +++ b/src/Module/Api/Mastodon/Bookmarks.php @@ -72,6 +72,7 @@ class Bookmarks extends BaseApi $statuses = []; while ($item = Post::fetch($items)) { + self::setBoundaries($item['uri-id']); $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid); } DBA::close($items); @@ -80,6 +81,7 @@ class Bookmarks extends BaseApi array_reverse($statuses); } + self::setLinkHeader(); System::jsonExit($statuses); } } diff --git a/src/Module/Api/Mastodon/Conversations.php b/src/Module/Api/Mastodon/Conversations.php index 0340478c6..22774b57c 100644 --- a/src/Module/Api/Mastodon/Conversations.php +++ b/src/Module/Api/Mastodon/Conversations.php @@ -85,6 +85,7 @@ class Conversations extends BaseApi $conversations = []; while ($conv = DBA::fetch($convs)) { + self::setBoundaries($conv['id']); $conversations[] = DI::mstdnConversation()->CreateFromConvId($conv['id']); } @@ -94,6 +95,7 @@ class Conversations extends BaseApi array_reverse($conversations); } + self::setLinkHeader(); System::jsonExit($conversations); } } diff --git a/src/Module/Api/Mastodon/Favourited.php b/src/Module/Api/Mastodon/Favourited.php index 69b102a27..239257e83 100644 --- a/src/Module/Api/Mastodon/Favourited.php +++ b/src/Module/Api/Mastodon/Favourited.php @@ -70,6 +70,7 @@ class Favourited extends BaseApi $statuses = []; while ($item = Post::fetch($items)) { + self::setBoundaries($item['thr-parent-id']); $statuses[] = DI::mstdnStatus()->createFromUriId($item['thr-parent-id'], $uid); } DBA::close($items); @@ -78,6 +79,7 @@ class Favourited extends BaseApi array_reverse($statuses); } + self::setLinkHeader(); System::jsonExit($statuses); } } diff --git a/src/Module/Api/Mastodon/FollowRequests.php b/src/Module/Api/Mastodon/FollowRequests.php index 462873845..cb7d5da22 100644 --- a/src/Module/Api/Mastodon/FollowRequests.php +++ b/src/Module/Api/Mastodon/FollowRequests.php @@ -92,8 +92,6 @@ class FollowRequests extends BaseApi 'limit' => 40, // Maximum number of results to return. Defaults to 40. Paginate using the HTTP Link header. ]); - $baseUrl = DI::baseUrl(); - $introductions = DI::intro()->selectByBoundaries( ['`uid` = ? AND NOT `ignore`', $uid], ['order' => ['id' => 'DESC']], @@ -106,6 +104,7 @@ class FollowRequests extends BaseApi foreach ($introductions as $key => $introduction) { try { + self::setBoundaries($introduction->id); $return[] = DI::mstdnFollowRequest()->createFromIntroduction($introduction); } catch (HTTPException\InternalServerErrorException $exception) { DI::intro()->delete($introduction); @@ -113,22 +112,7 @@ class FollowRequests extends BaseApi } } - $base_query = []; - if (isset($_GET['limit'])) { - $base_query['limit'] = $request['limit']; - } - - $links = []; - if ($introductions->getTotalCount() > $request['limit']) { - $links[] = '<' . $baseUrl->get() . '/api/v1/follow_requests?' . http_build_query($base_query + ['max_id' => $introductions[count($introductions) - 1]->id]) . '>; rel="next"'; - } - - if (count($introductions)) { - $links[] = '<' . $baseUrl->get() . '/api/v1/follow_requests?' . http_build_query($base_query + ['min_id' => $introductions[0]->id]) . '>; rel="prev"'; - } - - header('Link: ' . implode(', ', $links)); - + self::setLinkHeader(); System::jsonExit($return); } } diff --git a/src/Module/Api/Mastodon/Lists/Accounts.php b/src/Module/Api/Mastodon/Lists/Accounts.php index 21f85f316..6e10ad6bf 100644 --- a/src/Module/Api/Mastodon/Lists/Accounts.php +++ b/src/Module/Api/Mastodon/Lists/Accounts.php @@ -95,6 +95,7 @@ class Accounts extends BaseApi $members = DBA::select('group_member', ['contact-id'], $condition, $params); while ($member = DBA::fetch($members)) { + self::setBoundaries($member['contact-id']); $accounts[] = DI::mstdnAccount()->createFromContactId($member['contact-id'], $uid); } DBA::close($members); @@ -103,6 +104,7 @@ class Accounts extends BaseApi array_reverse($accounts); } + self::setLinkHeader(); System::jsonExit($accounts); } } diff --git a/src/Module/Api/Mastodon/Mutes.php b/src/Module/Api/Mastodon/Mutes.php index 469f3e0e6..ea890d9fd 100644 --- a/src/Module/Api/Mastodon/Mutes.php +++ b/src/Module/Api/Mastodon/Mutes.php @@ -77,6 +77,7 @@ class Mutes extends BaseApi $followers = DBA::select('user-contact', ['cid'], $condition, $parameters); while ($follower = DBA::fetch($followers)) { + self::setBoundaries($follower['cid']); $accounts[] = DI::mstdnAccount()->createFromContactId($follower['cid'], $uid); } DBA::close($followers); @@ -85,6 +86,7 @@ class Mutes extends BaseApi array_reverse($accounts); } + self::setLinkHeader(); System::jsonExit($accounts); } } diff --git a/src/Module/Api/Mastodon/Notifications.php b/src/Module/Api/Mastodon/Notifications.php index cf2a9827c..e02823dea 100644 --- a/src/Module/Api/Mastodon/Notifications.php +++ b/src/Module/Api/Mastodon/Notifications.php @@ -128,6 +128,7 @@ class Notifications extends BaseApi $notify = DBA::select('notification', ['id'], $condition, $params); while ($notification = DBA::fetch($notify)) { + self::setBoundaries($notification['id']); $entry = DI::mstdnNotification()->createFromNotificationId($notification['id']); if (!empty($entry)) { $notifications[] = $entry; @@ -138,6 +139,7 @@ class Notifications extends BaseApi array_reverse($notifications); } + self::setLinkHeader(); System::jsonExit($notifications); } } diff --git a/src/Module/Api/Mastodon/Search.php b/src/Module/Api/Mastodon/Search.php index 4ef4292f8..f6b95ae92 100644 --- a/src/Module/Api/Mastodon/Search.php +++ b/src/Module/Api/Mastodon/Search.php @@ -162,6 +162,7 @@ class Search extends BaseApi $statuses = []; while ($item = Post::fetch($items)) { + self::setBoundaries($item['uri-id']); $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid); } DBA::close($items); @@ -170,6 +171,7 @@ class Search extends BaseApi array_reverse($statuses); } + self::setLinkHeader(); return $statuses; } diff --git a/src/Module/Api/Mastodon/Timelines/Direct.php b/src/Module/Api/Mastodon/Timelines/Direct.php index adf677c72..c0fef79c5 100644 --- a/src/Module/Api/Mastodon/Timelines/Direct.php +++ b/src/Module/Api/Mastodon/Timelines/Direct.php @@ -71,6 +71,7 @@ class Direct extends BaseApi $statuses = []; while ($mail = DBA::fetch($mails)) { + self::setBoundaries($mail['uri-id']); $statuses[] = DI::mstdnStatus()->createFromMailId($mail['id']); } @@ -78,6 +79,7 @@ class Direct extends BaseApi array_reverse($statuses); } + self::setLinkHeader(); System::jsonExit($statuses); } } diff --git a/src/Module/Api/Mastodon/Timelines/Home.php b/src/Module/Api/Mastodon/Timelines/Home.php index 63d40e844..2b556d7ce 100644 --- a/src/Module/Api/Mastodon/Timelines/Home.php +++ b/src/Module/Api/Mastodon/Timelines/Home.php @@ -93,6 +93,7 @@ class Home extends BaseApi $statuses = []; while ($item = Post::fetch($items)) { + self::setBoundaries($item['uri-id']); $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid); } DBA::close($items); @@ -101,6 +102,7 @@ class Home extends BaseApi array_reverse($statuses); } + self::setLinkHeader(); System::jsonExit($statuses); } } diff --git a/src/Module/Api/Mastodon/Timelines/ListTimeline.php b/src/Module/Api/Mastodon/Timelines/ListTimeline.php index 22b2be3bc..8d4a432c5 100644 --- a/src/Module/Api/Mastodon/Timelines/ListTimeline.php +++ b/src/Module/Api/Mastodon/Timelines/ListTimeline.php @@ -98,6 +98,7 @@ class ListTimeline extends BaseApi $statuses = []; while ($item = Post::fetch($items)) { + self::setBoundaries($item['uri-id']); $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid); } DBA::close($items); @@ -106,6 +107,7 @@ class ListTimeline extends BaseApi array_reverse($statuses); } + self::setLinkHeader(); System::jsonExit($statuses); } } diff --git a/src/Module/Api/Mastodon/Timelines/PublicTimeline.php b/src/Module/Api/Mastodon/Timelines/PublicTimeline.php index 1efeabba8..bc617b65a 100644 --- a/src/Module/Api/Mastodon/Timelines/PublicTimeline.php +++ b/src/Module/Api/Mastodon/Timelines/PublicTimeline.php @@ -99,6 +99,7 @@ class PublicTimeline extends BaseApi $statuses = []; while ($item = Post::fetch($items)) { + self::setBoundaries($item['uri-id']); $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $item['uid']); } DBA::close($items); @@ -107,6 +108,7 @@ class PublicTimeline extends BaseApi array_reverse($statuses); } + self::setLinkHeader(); System::jsonExit($statuses); } } diff --git a/src/Module/Api/Mastodon/Timelines/Tag.php b/src/Module/Api/Mastodon/Timelines/Tag.php index 56697f2f5..0437be277 100644 --- a/src/Module/Api/Mastodon/Timelines/Tag.php +++ b/src/Module/Api/Mastodon/Timelines/Tag.php @@ -107,6 +107,7 @@ class Tag extends BaseApi $statuses = []; while ($item = Post::fetch($items)) { + self::setBoundaries($item['uri-id']); $statuses[] = DI::mstdnStatus()->createFromUriId($item['uri-id'], $uid); } DBA::close($items); @@ -115,6 +116,7 @@ class Tag extends BaseApi array_reverse($statuses); } + self::setLinkHeader(); System::jsonExit($statuses); } } diff --git a/src/Module/BaseApi.php b/src/Module/BaseApi.php index bae466c90..76acd4d88 100644 --- a/src/Module/BaseApi.php +++ b/src/Module/BaseApi.php @@ -44,6 +44,16 @@ class BaseApi extends BaseModule */ protected static $format = 'json'; + /** + * @var array + */ + protected static $boundaries = []; + + /** + * @var array + */ + protected static $request = []; + public static function init(array $parameters = []) { $arguments = DI::args(); @@ -129,6 +139,11 @@ class BaseApi extends BaseModule $httpinput = HTTPInputData::process(); $input = array_merge($httpinput['variables'], $httpinput['files'], $_REQUEST); + self::$request = $input; + self::$boundaries = []; + + unset(self::$request['pagename']); + $request = []; foreach ($defaults as $parameter => $defaultvalue) { @@ -160,6 +175,55 @@ class BaseApi extends BaseModule return $request; } + /** + * Set boundaries for the "link" header + * @param array $boundaries + * @param int $id + * @return array + */ + protected static function setBoundaries(int $id) + { + if (!isset(self::$boundaries['min'])) { + self::$boundaries['min'] = $id; + } + + if (!isset(self::$boundaries['max'])) { + self::$boundaries['max'] = $id; + } + + self::$boundaries['min'] = min(self::$boundaries['min'], $id); + self::$boundaries['max'] = max(self::$boundaries['max'], $id); + } + + /** + * Set the "link" header with "next" and "prev" links + * @return void + */ + protected static function setLinkHeader() + { + if (empty(self::$boundaries)) { + return; + } + + $request = self::$request; + + unset($request['min_id']); + unset($request['max_id']); + unset($request['since_id']); + + $prev_request = $next_request = $request; + + $prev_request['min_id'] = self::$boundaries['max'] + 1; + $next_request['max_id'] = self::$boundaries['min'] - 1; + + $command = DI::baseUrl() . '/' . DI::args()->getCommand(); + + $prev = $command . '?' . http_build_query($prev_request); + $next = $command . '?' . http_build_query($next_request); + + header('Link: <' . $next . '>; rel="next", <' . $prev . '>; rel="prev"'); + } + /** * Get current application token *