. /** * ActivityPub implementation for GNU social * * @package GNUsocial * * @author Diogo Cordeiro * @copyright 2018-2019 Free Software Foundation, Inc http://www.fsf.org * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later * * @see http://www.gnu.org/software/social/ */ defined('GNUSOCIAL') || die(); /** * Actor's Followers Collection * * @category Plugin * @package GNUsocial * * @author Diogo Cordeiro * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ class apActorFollowersAction extends ManagedAction { protected $needLogin = false; protected $canPost = true; /** * Handle the Followers Collection request * * @throws Exception * * @return void * * @author Diogo Cordeiro */ protected function handle() { try { $profile = Profile::getByID($this->trimmed('id')); $profile_id = $profile->getID(); } catch (Exception $e) { ActivityPubReturn::error('Invalid Actor URI.', 404); } if (!$profile->isLocal()) { ActivityPubReturn::error('This is not a local user.', 403); } if (!isset($_GET['page'])) { $page = 0; } else { $page = (int) ($this->trimmed('page')); } if ($page < 0) { ActivityPubReturn::error('Invalid page number.'); } $since = ($page - 1) * PROFILES_PER_MINILIST; $limit = PROFILES_PER_MINILIST; // Calculate total items $total_subs = Activitypub_profile::subscriberCount($profile); $total_pages = ceil($total_subs / PROFILES_PER_MINILIST); $res = [ '@context' => [ 'https://www.w3.org/ns/activitystreams', 'https://w3id.org/security/v1', ], 'id' => common_local_url('apActorFollowers', ['id' => $profile_id]) . (($page != 0) ? '?page=' . $page : ''), 'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'), 'totalItems' => $total_subs, ]; if ($page == 0) { $res['first'] = common_local_url('apActorFollowers', ['id' => $profile_id]) . '?page=1'; } else { $res['orderedItems'] = $this->generate_followers($profile, $since, $limit); $res['partOf'] = common_local_url('apActorFollowers', ['id' => $profile_id]); if ($page + 1 < $total_pages) { $res['next'] = common_local_url('apActorFollowers', ['id' => $profile_id]) . 'page=' . ($page + 1 == 1 ? 2 : $page + 1); } if ($page > 1) { $res['prev'] = common_local_url('apActorFollowers', ['id' => $profile_id]) . '?page=' . ($page - 1 <= 0 ? 1 : $page - 1); } } ActivityPubReturn::answer($res); } /** * Generates a list of stalkers for a given profile. * * @param Profile $profile * @param int $since * @param int $limit * * @throws Exception * * @return array of URIs * * @author Diogo Cordeiro */ public static function generate_followers($profile, $since, $limit) { $subs = []; try { $sub = Activitypub_profile::getSubscribers($profile, $since, $limit); // Get followers' URLs foreach ($sub as $s) { $subs[] = $s->getUri(); } } catch (NoResultException $e) { // Just let the exception go on its merry way } return $subs; } }