diff --git a/ActivityPubPlugin.php b/ActivityPubPlugin.php index 0c96be0..11e8045 100755 --- a/ActivityPubPlugin.php +++ b/ActivityPubPlugin.php @@ -184,6 +184,12 @@ class ActivityPubPlugin extends Plugin ['id' => '[0-9]+'] ); + $m->connect( + 'user/:id/outbox.json', + ['action' => 'apActorOutbox'], + ['id' => '[0-9]+'] + ); + $m->connect( 'inbox.json', ['action' => 'apInbox'] diff --git a/actions/apactorfollowers.php b/actions/apactorfollowers.php index 4eadac7..007e8ff 100755 --- a/actions/apactorfollowers.php +++ b/actions/apactorfollowers.php @@ -59,7 +59,7 @@ class apActorFollowersAction extends ManagedAction } if (!$profile->isLocal()) { - ActivityPubReturn::error("This is not a local user."); + ActivityPubReturn::error("This is not a local user.", 403); } if (!isset($_GET["page"])) { @@ -75,23 +75,10 @@ class apActorFollowersAction extends ManagedAction $since = ($page - 1) * PROFILES_PER_MINILIST; $limit = (($page - 1) == 0 ? 1 : $page) * PROFILES_PER_MINILIST; - /* Fetch Followers */ - try { - $sub = $profile->getSubscribers($since, $limit); - } catch (NoResultException $e) { - // Just let the exception go on its merry way - } - /* Calculate total items */ $total_subs = $profile->subscriberCount(); $total_pages = ceil($total_subs / PROFILES_PER_MINILIST); - /* Get followers' URLs */ - $subs = array(); - while ($sub->fetch()) { - $subs[] = ActivityPubPlugin::actor_uri($sub); - } - $res = [ '@context' => [ "https://www.w3.org/ns/activitystreams", @@ -99,13 +86,13 @@ class apActorFollowersAction extends ManagedAction ], 'id' => common_local_url('apActorFollowers', ['id' => $profile_id]).(($page != 0) ? '?page='.$page : ''), 'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'), - 'totalItems' => $total_subs, - 'orderedItems' => $subs + '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) { @@ -119,4 +106,30 @@ class apActorFollowersAction extends ManagedAction ActivityPubReturn::answer($res); } + + /** + * Generates a list of followers for a given profile. + * + * @author Diogo Cordeiro + * @param Profile $profile + * @param int32 $since + * @param int32 $limit + * @return Array of URIs + */ + public function generate_followers($profile, $since, $limit) + { + /* Fetch Followers */ + try { + $sub = $profile->getSubscribers($since, $limit); + } catch (NoResultException $e) { + // Just let the exception go on its merry way + } + + /* Get followers' URLs */ + $subs = []; + while ($sub->fetch()) { + $subs[] = ActivityPubPlugin::actor_uri($sub); + } + return $subs; + } } diff --git a/actions/apactorfollowing.php b/actions/apactorfollowing.php index 7fa71c1..24f4221 100755 --- a/actions/apactorfollowing.php +++ b/actions/apactorfollowing.php @@ -59,7 +59,7 @@ class apActorFollowingAction extends ManagedAction } if (!$profile->isLocal()) { - ActivityPubReturn::error("This is not a local user."); + ActivityPubReturn::error("This is not a local user.", 403); } if (!isset($_GET["page"])) { @@ -75,23 +75,10 @@ class apActorFollowingAction extends ManagedAction $since = ($page - 1) * PROFILES_PER_MINILIST; $limit = (($page - 1) == 0 ? 1 : $page) * PROFILES_PER_MINILIST; - /* Fetch Following */ - try { - $sub = $profile->getSubscribed($since, $limit); - } catch (NoResultException $e) { - // Just let the exception go on its merry way - } - /* Calculate total items */ $total_subs = $profile->subscriptionCount(); $total_pages = ceil($total_subs / PROFILES_PER_MINILIST); - /* Get followed' URLs */ - $subs = array(); - while ($sub->fetch()) { - $subs[] = ActivityPubPlugin::actor_uri($sub); - } - $res = [ '@context' => [ "https://www.w3.org/ns/activitystreams", @@ -99,13 +86,13 @@ class apActorFollowingAction extends ManagedAction ], 'id' => common_local_url('apActorFollowing', ['id' => $profile_id]).(($page != 0) ? '?page='.$page : ''), 'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'), - 'totalItems' => $total_subs, - 'orderedItems' => $subs + 'totalItems' => $total_subs ]; if ($page == 0) { $res['first'] = common_local_url('apActorFollowing', ['id' => $profile_id]).'?page=1'; } else { + $res['orderedItems'] = $this->generate_following($profile, $since, $limit); $res['partOf'] = common_local_url('apActorFollowing', ['id' => $profile_id]); if ($page+1 < $total_pages) { @@ -119,4 +106,30 @@ class apActorFollowingAction extends ManagedAction ActivityPubReturn::answer($res); } + + /** + * Generates a list of people following given profile. + * + * @author Diogo Cordeiro + * @param Profile $profile + * @param int32 $since + * @param int32 $limit + * @return Array of URIs + */ + public function generate_following($profile, $since, $limit) + { + /* Fetch Following */ + try { + $sub = $profile->getSubscribed($since, $limit); + } catch (NoResultException $e) { + // Just let the exception go on its merry way + } + + /* Get followed' URLs */ + $subs = []; + while ($sub->fetch()) { + $subs[] = ActivityPubPlugin::actor_uri($sub); + } + return $subs; + } } diff --git a/actions/apactorliked.php b/actions/apactorliked.php index ff1fe46..e810c00 100755 --- a/actions/apactorliked.php +++ b/actions/apactorliked.php @@ -53,13 +53,13 @@ class apActorLikedAction extends ManagedAction { try { $profile = Profile::getByID($this->trimmed('id')); - $url = ActivityPubPlugin::actor_url($profile); + $profile_id = $profile->getID(); } catch (Exception $e) { ActivityPubReturn::error('Invalid Actor URI.', 404); } if (!$profile->isLocal()) { - ActivityPubReturn::error("This is not a local user."); + ActivityPubReturn::error("This is not a local user.", 403); } $limit = intval($this->trimmed('limit')); @@ -75,7 +75,7 @@ class apActorLikedAction extends ManagedAction $limit = 80; } - $fave = $this->fetch_faves($profile->getID(), $limit, $since_id, $max_id); + $fave = $this->fetch_faves($profile_id, $limit, $since_id, $max_id); $faves = array(); while ($fave->fetch()) { @@ -83,17 +83,15 @@ class apActorLikedAction extends ManagedAction } $res = [ - '@context' => [ - "https://www.w3.org/ns/activitystreams", - [ - "@language" => "en" - ] - ], - 'id' => "{$url}/liked.json", - 'type' => 'OrderedCollection', - 'totalItems' => Fave::countByProfile($profile), - 'orderedItems' => $faves - ]; + '@context' => [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + ], + 'id' => common_local_url('apActorLiked', ['id' => $profile_id]), + 'type' => 'OrderedCollection', + 'totalItems' => Fave::countByProfile($profile), + 'orderedItems' => $faves + ]; ActivityPubReturn::answer($res); } @@ -130,7 +128,7 @@ class apActorLikedAction extends ManagedAction $user_id, $limit = 40, $since_id = null, - $max_id = null + $max_id = null ) { $fav = new Fave(); diff --git a/actions/apactoroutbox.php b/actions/apactoroutbox.php new file mode 100644 index 0000000..60986d8 --- /dev/null +++ b/actions/apactoroutbox.php @@ -0,0 +1,132 @@ +. + * + * @category Plugin + * @package GNUsocial + * @author Diogo Cordeiro + * @author Daniel Supernault + * @copyright 2018 Free Software Foundation http://fsf.org + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link https://www.gnu.org/software/social/ + */ +if (!defined('GNUSOCIAL')) { + exit(1); +} + +/** + * Inbox Request Handler + * + * @category Plugin + * @package GNUsocial + * @author Diogo Cordeiro + * @license http://www.fsf.org/licensing/licenses/agpl-3.0.html GNU Affero General Public License version 3.0 + * @link http://www.gnu.org/software/social/ + */ +class apActorOutboxAction extends ManagedAction +{ + protected $needLogin = false; + protected $canPost = true; + + /** + * Handle the Outbox request + * + * @author Daniel Supernault + */ + 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 = intval($this->trimmed('page')); + } + + if ($page < 0) { + ActivityPubReturn::error('Invalid page number.'); + } + + $since = ($page - 1) * PROFILES_PER_MINILIST; + $limit = (($page - 1) == 0 ? 1 : $page) * PROFILES_PER_MINILIST; + + /* Calculate total items */ + $total_notes = $profile->noticeCount(); + $total_pages = ceil($total_notes / PROFILES_PER_MINILIST); + + $res = [ + '@context' => [ + "https://www.w3.org/ns/activitystreams", + "https://w3id.org/security/v1", + ], + 'id' => common_local_url('apActorOutbox', ['id' => $profile_id]).(($page != 0) ? '?page='.$page : ''), + 'type' => ($page == 0 ? 'OrderedCollection' : 'OrderedCollectionPage'), + 'totalItems' => $total_notes + ]; + + if ($page == 0) { + $res['first'] = common_local_url('apActorOutbox', ['id' => $profile_id]).'?page=1'; + } else { + $res['orderedItems'] = $this->generate_outbox($profile); + $res['partOf'] = common_local_url('apActorOutbox', ['id' => $profile_id]); + + if ($page+1 < $total_pages) { + $res['next'] = common_local_url('apActorOutbox', ['id' => $profile_id]).'page='.($page+1 == 1 ? 2 : $page+1); + } + + if ($page > 1) { + $res['prev'] = common_local_url('apActorOutbox', ['id' => $profile_id]).'?page='.($page-1 <= 0 ? 1 : $page-1); + } + } + + ActivityPubReturn::answer($res); + } + + /** + * Generates a list of people following given profile. + * + * @author Daniel Supernault + * @param Profile $profile + * @return Array of Notices + */ + public function generate_outbox($profile) + { + /* Fetch Notices */ + $notices = []; + $notice = $profile->getNotices(); + while ($notice->fetch()) { + $note = $notice; + + // TODO: Handle other types + if ($note->object_type == 'http://activitystrea.ms/schema/1.0/note') { + $notices[] = Activitypub_notice::notice_to_array($note); + } + } + + return $notices; + } +} diff --git a/actions/apactorprofile.php b/actions/apactorprofile.php index d37093c..96babfb 100755 --- a/actions/apactorprofile.php +++ b/actions/apactorprofile.php @@ -67,7 +67,7 @@ class apActorProfileAction extends ManagedAction } if (!$profile->isLocal()) { - ActivityPubReturn::error("This is not a local user."); + ActivityPubReturn::error("This is not a local user.", 403); } $res = Activitypub_profile::profile_to_array($profile); diff --git a/actions/apnotice.php b/actions/apnotice.php index 8222ae7..949dbf9 100755 --- a/actions/apnotice.php +++ b/actions/apnotice.php @@ -58,7 +58,7 @@ class apNoticeAction extends ManagedAction } if (!$notice->isLocal()) { - ActivityPubReturn::error("This is not a local notice."); + ActivityPubReturn::error("This is not a local notice.", 403); } $res = Activitypub_notice::notice_to_array($notice); diff --git a/classes/Activitypub_profile.php b/classes/Activitypub_profile.php index 6e7f491..8f6af35 100755 --- a/classes/Activitypub_profile.php +++ b/classes/Activitypub_profile.php @@ -102,10 +102,11 @@ class Activitypub_profile extends Managed_DataObject ], 'id' => $uri, 'type' => 'Person', - 'following' => common_local_url("apActorFollowing", array("id" => $id)), - 'followers' => common_local_url("apActorFollowers", array("id" => $id)), - 'liked' => common_local_url("apActorLiked", array("id" => $id)), - 'inbox' => common_local_url("apInbox", array("id" => $id)), + 'following' => common_local_url('apActorFollowing', ['id' => $id]), + 'followers' => common_local_url('apActorFollowers', ['id' => $id]), + 'liked' => common_local_url('apActorLiked', ['id' => $id]), + 'inbox' => common_local_url('apInbox', ['id' => $id]), + 'outbox' => common_local_url('apActorOutbox', ['id' => $id]), 'preferredUsername' => $profile->getNickname(), 'name' => $profile->getBestName(), 'summary' => ($desc = $profile->getDescription()) == null ? "" : $desc,