From 416451a5192ca549f15468e133f8c278a233dea7 Mon Sep 17 00:00:00 2001 From: Diogo Peralta Cordeiro Date: Thu, 10 Feb 2022 16:02:51 +0000 Subject: [PATCH] [CORE][Actor] Simplify logic so more is reused between different types of actors Minor bug fixes --- .../Collection/Util/ActorControllerTrait.php | 71 -------------- components/Group/Controller/Group.php | 34 ++++--- components/Group/Entity/GroupJoinQueue.php | 2 +- components/Group/Entity/GroupMember.php | 12 +-- components/Group/Entity/LocalGroup.php | 32 +++---- components/Group/Group.php | 12 +-- components/Person/Controller/PersonFeed.php | 84 ++++++++++++++++ .../Person/Controller/PersonSettings.php | 24 +++-- components/Person/Person.php | 39 ++++++++ .../Subscription/Controller/Subscribers.php | 44 +++++---- .../Subscription/Controller/Subscriptions.php | 43 +++++---- components/Subscription/Subscription.php | 3 - plugins/ActivityPub/ActivityPub.php | 8 +- plugins/ActivityPub/Util/Model/Actor.php | 3 +- src/Controller/ActorFeed.php | 37 +++---- src/Entity/Actor.php | 14 +-- src/Entity/Note.php | 1 - src/Entity/RelatedGroup.php | 96 ------------------- src/Routes/Actor.php | 2 - src/Routes/Main.php | 2 - templates/cards/blocks/navigation.html.twig | 2 +- tests/Controller/UserPanelTest.php | 16 ++-- 22 files changed, 264 insertions(+), 317 deletions(-) delete mode 100644 components/Collection/Util/ActorControllerTrait.php create mode 100644 components/Person/Controller/PersonFeed.php rename src/Controller/UserPanel.php => components/Person/Controller/PersonSettings.php (94%) create mode 100644 components/Person/Person.php delete mode 100644 src/Entity/RelatedGroup.php diff --git a/components/Collection/Util/ActorControllerTrait.php b/components/Collection/Util/ActorControllerTrait.php deleted file mode 100644 index 5f7b1569c1..0000000000 --- a/components/Collection/Util/ActorControllerTrait.php +++ /dev/null @@ -1,71 +0,0 @@ -. -// }}} - -/** - * Base class for feed controllers - * - * @package GNUsocial - * @category Controller - * - * @author Hugo Sales - * @copyright 2021 Free Software Foundation, Inc http://www.fsf.org - * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later - */ - -namespace Component\Collection\Util; - -use App\Core\DB\DB; -use function App\Core\I18n\_m; -use App\Core\Router\Router; -use App\Util\Exception\ClientException; - -trait ActorControllerTrait -{ - /** - * Generic function that handles getting a representation for an actor from id - */ - protected function handleActorById(int $id, callable $handle) - { - $actor = DB::findOneBy('actor', ['id' => $id]); - if ($actor->getIsLocal()) { - return ['_redirect' => $actor->getUrl(Router::ABSOLUTE_PATH), 'actor' => $actor]; - } - if (empty($actor)) { - throw new ClientException(_m('No such actor.'), 404); - } else { - return $handle($actor); - } - } - - /** - * Generic function that handles getting a representation for an actor from nickname - */ - protected function handleActorByNickname(string $nickname, callable $handle) - { - $user = DB::findOneBy('local_user', ['nickname' => $nickname]); - $actor = DB::findOneBy('actor', ['id' => $user->getId()]); - if (empty($actor)) { - throw new ClientException(_m('No such actor.'), 404); - } else { - return $handle($actor); - } - } -} diff --git a/components/Group/Controller/Group.php b/components/Group/Controller/Group.php index a9c5552fc9..179a694c51 100644 --- a/components/Group/Controller/Group.php +++ b/components/Group/Controller/Group.php @@ -29,6 +29,7 @@ use App\Core\DB\DB; use App\Core\Form; use function App\Core\I18n\_m; use App\Core\Log; +use App\Core\Router\Router; use App\Entity\Actor; use App\Entity as E; use App\Util\Common; @@ -44,7 +45,6 @@ use App\Util\Exception\NotFoundException; use App\Util\Exception\RedirectException; use App\Util\Exception\ServerException; use App\Util\Form\ActorForms; -use App\Util\Nickname; use Component\Collection\Util\Controller\FeedController; use Component\Group\Entity\GroupMember; use Component\Group\Entity\LocalGroup; @@ -60,7 +60,7 @@ class Group extends FeedController /** * @throws ServerException */ - public function handleGroup(Request $request, Actor $group): array + public function groupView(Request $request, Actor $group): array { $actor = Common::actor(); $subscribe_form = null; @@ -106,9 +106,23 @@ class Group extends FeedController ]; } + /** + * @throws ClientException + * @throws ServerException + */ public function groupViewId(Request $request, int $id): array { - return $this->handleGroup($request, Actor::getById($id)); + $group = Actor::getById($id); + if (\is_null($group) || !$group->isGroup()) { + throw new ClientException(_m('No such group.'), 404); + } + if ($group->getIsLocal()) { + return [ + '_redirect' => Router::url('group_actor_view_nickname', ['nickname' => $group->getNickname()]), + 'actor' => $group, + ]; + } + return $this->groupView($request, $group); } /** @@ -116,20 +130,16 @@ class Group extends FeedController * * @param string $nickname The group's nickname to be shown * - * @throws NicknameEmptyException - * @throws NicknameNotAllowedException - * @throws NicknameTakenException - * @throws NicknameTooLongException + * @throws ClientException * @throws ServerException */ public function groupViewNickname(Request $request, string $nickname): array { - Nickname::validate($nickname, which: Nickname::CHECK_LOCAL_GROUP); // throws $group = LocalGroup::getActorByNickname($nickname); if (\is_null($group)) { - throw new NotFoundException(_m('Group not found.')); + throw new ClientException(_m('No such group.'), 404); } - return $this->handleGroup($request, $group); + return $this->groupView($request, $group); } /** @@ -168,7 +178,7 @@ class Group extends FeedController */ public function groupSettings(Request $request, int $id): array { - $local_group = DB::findOneBy(LocalGroup::class, ['group_id' => $id]); + $local_group = DB::findOneBy(LocalGroup::class, ['actor_id' => $id]); $group_actor = $local_group->getActor(); $actor = Common::actor(); if (!\is_null($group_actor) && $actor->canAdmin($group_actor)) { @@ -219,7 +229,7 @@ class Group extends FeedController 'roles' => ActorLocalRoles::VISITOR, // Can send direct messages to other actors ])); DB::persist(LocalGroup::create([ - 'group_id' => $group->getId(), + 'actor_id' => $group->getId(), 'type' => $data['group_type'], 'nickname' => $nickname, ])); diff --git a/components/Group/Entity/GroupJoinQueue.php b/components/Group/Entity/GroupJoinQueue.php index f9935ab408..1be66ca366 100644 --- a/components/Group/Entity/GroupJoinQueue.php +++ b/components/Group/Entity/GroupJoinQueue.php @@ -76,7 +76,7 @@ class GroupJoinQueue extends Entity 'description' => 'Holder for group join requests awaiting moderation.', 'fields' => [ 'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'many to one', 'name' => 'group_join_queue_actor_id_fkey', 'not null' => true, 'description' => 'remote or local actor making the request'], - 'group_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Group.id', 'multiplicity' => 'many to one', 'name' => 'group_join_queue_group_id_fkey', 'not null' => true, 'description' => 'remote or local group to join, if any'], + 'group_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Group.id', 'multiplicity' => 'many to one', 'name' => 'group_join_queue_group_id_fkey', 'not null' => true, 'description' => 'remote or local group to join, if any'], ], 'primary key' => ['actor_id', 'group_id'], 'indexes' => [ diff --git a/components/Group/Entity/GroupMember.php b/components/Group/Entity/GroupMember.php index 44ca47a249..c1b4b3ccd4 100644 --- a/components/Group/Entity/GroupMember.php +++ b/components/Group/Entity/GroupMember.php @@ -123,12 +123,12 @@ class GroupMember extends Entity return [ 'name' => 'group_member', 'fields' => [ - 'group_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'name' => 'group_member_group_id_fkey', 'not null' => true, 'description' => 'foreign key to group table'], - 'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'name' => 'group_member_actor_id_fkey', 'not null' => true, 'description' => 'foreign key to actor table'], - 'is_admin' => ['type' => 'bool', 'default' => false, 'description' => 'is this actor an admin?'], - 'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'], - 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], - 'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], + 'group_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'name' => 'group_member_group_id_fkey', 'not null' => true, 'description' => 'foreign key to group table'], + 'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Actor.id', 'multiplicity' => 'one to one', 'name' => 'group_member_actor_id_fkey', 'not null' => true, 'description' => 'foreign key to actor table'], + 'is_admin' => ['type' => 'bool', 'default' => false, 'description' => 'is this actor an admin?'], + 'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'], + 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], + 'modified' => ['type' => 'timestamp', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], ], 'primary key' => ['group_id', 'actor_id'], 'unique keys' => [ diff --git a/components/Group/Entity/LocalGroup.php b/components/Group/Entity/LocalGroup.php index 059679e783..c54d5b384d 100644 --- a/components/Group/Entity/LocalGroup.php +++ b/components/Group/Entity/LocalGroup.php @@ -21,10 +21,8 @@ declare(strict_types = 1); namespace Component\Group\Entity; -use App\Core\Cache; use App\Core\DB\DB; use App\Core\Entity; - use App\Entity\Actor; use App\Util\Exception\NicknameEmptyException; use App\Util\Exception\NicknameException; @@ -53,21 +51,21 @@ class LocalGroup extends Entity { // {{{ Autocode // @codeCoverageIgnoreStart - private int $group_id; + private int $actor_id; private string $nickname; private string $type; private DateTimeInterface $created; private DateTimeInterface $modified; - public function setGroupId(int $group_id): self + public function setActorId(int $actor_id): self { - $this->group_id = $group_id; + $this->actor_id = $actor_id; return $this; } - public function getGroupId(): int + public function getActorId(): int { - return $this->group_id; + return $this->actor_id; } public function setNickname(string $nickname): self @@ -119,19 +117,17 @@ class LocalGroup extends Entity public function getActor() { - return DB::find('actor', ['id' => $this->group_id]); + return DB::findOneBy('actor', ['id' => $this->actor_id]); } public static function getByNickname(string $nickname): ?self { - $res = DB::findBy(self::class, ['nickname' => $nickname]); - return $res === [] ? null : $res[0]; + return DB::findOneBy(self::class, ['nickname' => $nickname]); } public static function getActorByNickname(string $nickname): ?Actor { - $res = DB::findBy(Actor::class, ['nickname' => $nickname, 'type' => Actor::GROUP]); - return $res === [] ? null : $res[0]; + return DB::findOneBy(Actor::class, ['nickname' => $nickname, 'type' => Actor::GROUP]); } /** @@ -163,13 +159,13 @@ class LocalGroup extends Entity 'name' => 'local_group', 'description' => 'Record for a user group on the local site, with some additional info not in user_group', 'fields' => [ - 'group_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Group.id', 'multiplicity' => 'one to one', 'name' => 'local_group_group_id_fkey', 'not null' => true, 'description' => 'group represented'], - 'nickname' => ['type' => 'varchar', 'not null' => true, 'length' => 64, 'description' => 'group represented'], - 'type' => ['type' => 'varchar', 'not null' => true, 'default' => 'group', 'length' => 64, 'description' => 'Group or Organisation'], - 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], - 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], + 'actor_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Group.id', 'multiplicity' => 'one to one', 'name' => 'local_group_group_id_fkey', 'not null' => true, 'description' => 'group represented'], + 'nickname' => ['type' => 'varchar', 'not null' => true, 'length' => 64, 'description' => 'group represented'], + 'type' => ['type' => 'varchar', 'not null' => true, 'default' => 'group', 'length' => 64, 'description' => 'Group or Organisation'], + 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], + 'modified' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was modified'], ], - 'primary key' => ['group_id'], + 'primary key' => ['actor_id'], 'unique keys' => [ 'local_group_nickname_key' => ['nickname'], ], diff --git a/components/Group/Group.php b/components/Group/Group.php index c5394dc065..fcff9bb481 100644 --- a/components/Group/Group.php +++ b/components/Group/Group.php @@ -39,10 +39,10 @@ class Group extends Component { public function onAddRoute(RouteLoader $r): bool { - $r->connect(id: 'group_create', uri_path: '/group/new', target: [C\Group::class, 'groupCreate']); $r->connect(id: 'group_actor_view_id', uri_path: '/group/{id<\d+>}', target: [C\Group::class, 'groupViewId']); - $r->connect(id: 'group_settings', uri_path: '/group/{id<\d+>}/settings', target: [C\Group::class, 'groupSettings']); $r->connect(id: 'group_actor_view_nickname', uri_path: '/!{nickname<' . Nickname::DISPLAY_FMT . '>}', target: [C\Group::class, 'groupViewNickname']); + $r->connect(id: 'group_create', uri_path: '/group/new', target: [C\Group::class, 'groupCreate']); + $r->connect(id: 'group_settings', uri_path: '/group/{id<\d+>}/settings', target: [C\Group::class, 'groupSettings']); return Event::next; } @@ -60,7 +60,7 @@ class Group extends Component return Event::next; } - public function onPopulateSettingsTabs(Request $request, string $section, array &$tabs) + public function onPopulateSettingsTabs(Request $request, string $section, array &$tabs): bool { if ($section === 'profile' && $request->get('_route') === 'group_settings') { $group_id = $request->get('id'); @@ -92,7 +92,7 @@ class Group extends Component return null; } - public function onPostingFillTargetChoices(Request $request, Actor $actor, array &$targets) + public function onPostingFillTargetChoices(Request $request, Actor $actor, array &$targets): bool { $group = $this->getGroupFromContext($request); if (!\is_null($group)) { @@ -110,10 +110,8 @@ class Group extends Component * in the Posting's form. * * @param null|Actor $context_actor Actor group, if current route is part of an existing Group set of routes - * - * @return bool */ - public function onPostingGetContextActor(Request $request, Actor $actor, ?Actor &$context_actor) + public function onPostingGetContextActor(Request $request, Actor $actor, ?Actor &$context_actor): bool { $ctx = $this->getGroupFromContext($request); if (!\is_null($ctx)) { diff --git a/components/Person/Controller/PersonFeed.php b/components/Person/Controller/PersonFeed.php new file mode 100644 index 0000000000..29290ed427 --- /dev/null +++ b/components/Person/Controller/PersonFeed.php @@ -0,0 +1,84 @@ +. + +// }}} + +namespace Component\Person\Controller; + +use function App\Core\I18n\_m; +use App\Core\Router\Router; +use App\Entity\Actor; +use App\Entity as E; +use App\Entity\LocalUser; +use App\Util\Exception\ClientException; +use App\Util\Exception\ServerException; +use Component\Collection\Util\Controller\FeedController; +use Symfony\Component\HttpFoundation\Request; + +class PersonFeed extends FeedController +{ + /** + * @throws ClientException + * @throws ServerException + */ + public function personViewId(Request $request, int $id): array + { + $person = Actor::getById($id); + if (\is_null($person) || !$person->isPerson()) { + throw new ClientException(_m('No such person.'), 404); + } + if ($person->getIsLocal()) { + return [ + '_redirect' => Router::url('person_actor_view_nickname', ['nickname' => $person->getNickname()]), + 'actor' => $person, + ]; + } + return $this->personView($request, $person); + } + + /** + * View a group feed by its nickname + * + * @param string $nickname The group's nickname to be shown + * + * @throws ClientException + * @throws ServerException + */ + public function personViewNickname(Request $request, string $nickname): array + { + $user = LocalUser::getByNickname($nickname); + if (\is_null($user)) { + throw new ClientException(_m('No such person.'), 404); + } + $person = Actor::getById($user->getId()); + return $this->personView($request, $person); + } + + public function personView(Request $request, Actor $person): array + { + return [ + '_template' => 'actor/view.html.twig', + 'actor' => $person, + 'nickname' => $person->getNickname(), + 'notes' => E\Note::getAllNotesByActor($person), + ]; + } +} diff --git a/src/Controller/UserPanel.php b/components/Person/Controller/PersonSettings.php similarity index 94% rename from src/Controller/UserPanel.php rename to components/Person/Controller/PersonSettings.php index 0fd742107d..1765c16a4c 100644 --- a/src/Controller/UserPanel.php +++ b/components/Person/Controller/PersonSettings.php @@ -33,7 +33,7 @@ declare(strict_types = 1); * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later */ -namespace App\Controller; +namespace Component\Person\Controller; // {{{ Imports @@ -45,7 +45,13 @@ use function App\Core\I18n\_m; use App\Core\Log; use App\Util\Common; use App\Util\Exception\AuthenticationException; +use App\Util\Exception\NicknameEmptyException; +use App\Util\Exception\NicknameInvalidException; +use App\Util\Exception\NicknameNotAllowedException; +use App\Util\Exception\NicknameTakenException; +use App\Util\Exception\NicknameTooLongException; use App\Util\Exception\NoLoggedInUser; +use App\Util\Exception\RedirectException; use App\Util\Exception\ServerException; use App\Util\Form\ActorArrayTransformer; use App\Util\Form\ActorForms; @@ -64,20 +70,22 @@ use Symfony\Component\HttpFoundation\Request; // }}} Imports -class UserPanel extends Controller +class PersonSettings extends Controller { /** * Return main settings page forms * - * @throws \App\Util\Exception\NicknameEmptyException - * @throws \App\Util\Exception\NicknameInvalidException - * @throws \App\Util\Exception\NicknameNotAllowedException - * @throws \App\Util\Exception\NicknameTakenException - * @throws \App\Util\Exception\NicknameTooLongException - * @throws \App\Util\Exception\RedirectException + * @throws \App\Util\Exception\ClientException + * @throws \App\Util\Exception\NicknameException * @throws \Doctrine\DBAL\Exception * @throws AuthenticationException + * @throws NicknameEmptyException + * @throws NicknameInvalidException + * @throws NicknameNotAllowedException + * @throws NicknameTakenException + * @throws NicknameTooLongException * @throws NoLoggedInUser + * @throws RedirectException * @throws ServerException */ public function allSettings(Request $request, LanguageController $language): array diff --git a/components/Person/Person.php b/components/Person/Person.php new file mode 100644 index 0000000000..b6e64f0a28 --- /dev/null +++ b/components/Person/Person.php @@ -0,0 +1,39 @@ +. +// }}} + +namespace Component\Person; + +use App\Core\Event; +use App\Core\Modules\Component; +use App\Core\Router\RouteLoader; +use App\Util\Nickname; +use Component\Person\Controller as C; + +class Person extends Component +{ + public function onAddRoute(RouteLoader $r): bool + { + $r->connect(id: 'person_actor_view_id', uri_path: '/person/{id<\d+>}', target: [C\PersonFeed::class, 'personViewId']); + $r->connect(id: 'person_actor_view_nickname', uri_path: '/@{nickname<' . Nickname::DISPLAY_FMT . '>}', target: [C\PersonFeed::class, 'personViewNickname'], options: ['is_system_path' => false]); + $r->connect(id: 'person_actor_settings', uri_path: '/person/{id<\d+>}/settings', target: [C\PersonSettings::class, 'allSettings']); + return Event::next; + } +} diff --git a/components/Subscription/Controller/Subscribers.php b/components/Subscription/Controller/Subscribers.php index 034259e6dc..cdc19f3130 100644 --- a/components/Subscription/Controller/Subscribers.php +++ b/components/Subscription/Controller/Subscribers.php @@ -32,7 +32,7 @@ use App\Entity\Actor; use App\Util\Common; use App\Util\Exception\ClientException; use App\Util\Exception\RedirectException; -use Component\Collection\Util\ActorControllerTrait; +use App\Util\Exception\ServerException; use Component\Collection\Util\Controller\CircleController; use Component\Subscription\Subscription as SubscriptionComponent; use Symfony\Component\Form\Extension\Core\Type\SubmitType; @@ -43,30 +43,32 @@ use Symfony\Component\HttpFoundation\Request; */ class Subscribers extends CircleController { - use ActorControllerTrait; - public function subscribersByActorId(Request $request, int $id) + /** + * @throws ServerException + */ + public function subscribersByActor(Request $request, Actor $actor): array { - return $this->handleActorById( - $id, - fn ($actor) => [ - 'actor' => $actor, - ], - ); + return [ + '_template' => 'collection/actors.html.twig', + 'title' => _m('Subscribers'), + 'empty_message' => _m('No subscribers.'), + 'sort_form_fields' => [], + 'page' => $this->int('page') ?? 1, + 'actors' => $actor->getSubscribers(), + ]; } - public function subscribersByActorNickname(Request $request, string $nickname) + /** + * @throws ClientException + * @throws ServerException + */ + public function subscribersByActorId(Request $request, int $id): array { - return $this->handleActorByNickname( - $nickname, - fn ($actor) => [ - '_template' => 'collection/actors.html.twig', - 'title' => _m('Subscribers'), - 'empty_message' => _m('No subscribers.'), - 'sort_form_fields' => [], - 'page' => $this->int('page') ?? 1, - 'actors' => $actor->getSubscribers(), - ], - ); + $actor = Actor::getById($id); + if (\is_null($actor)) { + throw new ClientException(_m('No such actor.'), 404); + } + return $this->subscribersByActor($request, $actor); } /** diff --git a/components/Subscription/Controller/Subscriptions.php b/components/Subscription/Controller/Subscriptions.php index dbf3d691fc..3e6fc17a75 100644 --- a/components/Subscription/Controller/Subscriptions.php +++ b/components/Subscription/Controller/Subscriptions.php @@ -24,7 +24,9 @@ declare(strict_types = 1); namespace Component\Subscription\Controller; use function App\Core\I18n\_m; -use Component\Collection\Util\ActorControllerTrait; +use App\Entity\Actor; +use App\Util\Exception\ClientException; +use App\Util\Exception\ServerException; use Component\Collection\Util\Controller\CircleController; use Symfony\Component\HttpFoundation\Request; @@ -33,29 +35,28 @@ use Symfony\Component\HttpFoundation\Request; */ class Subscriptions extends CircleController { - use ActorControllerTrait; - public function subscriptionsByActorId(Request $request, int $id) + /** + * @throws ClientException + * @throws ServerException + */ + public function subscriptionsByActorId(Request $request, int $id): array { - return $this->handleActorById( - $id, - fn ($actor) => [ - 'actor' => $actor, - ], - ); + $actor = Actor::getById($id); + if (\is_null($actor)) { + throw new ClientException(_m('No such actor.'), 404); + } + return $this->subscriptionsByActor($request, $actor); } - public function subscriptionsByActorNickname(Request $request, string $nickname) + public function subscriptionsByActor(Request $request, Actor $actor) { - return $this->handleActorByNickname( - $nickname, - fn ($actor) => [ - '_template' => 'collection/actors.html.twig', - 'title' => _m('Subscriptions'), - 'empty_message' => _m('Haven\'t subscribed anyone.'), - 'sort_form_fields' => [], - 'page' => $this->int('page') ?? 1, - 'actors' => $actor->getSubscribers(), - ], - ); + return [ + '_template' => 'collection/actors.html.twig', + 'title' => _m('Subscriptions'), + 'empty_message' => _m('Haven\'t subscribed anyone.'), + 'sort_form_fields' => [], + 'page' => $this->int('page') ?? 1, + 'actors' => $actor->getSubscribers(), + ]; } } diff --git a/components/Subscription/Subscription.php b/components/Subscription/Subscription.php index 4a04838713..37f91c6beb 100644 --- a/components/Subscription/Subscription.php +++ b/components/Subscription/Subscription.php @@ -37,7 +37,6 @@ use App\Util\Common; use App\Util\Exception\DuplicateFoundException; use App\Util\Exception\NotFoundException; use App\Util\Exception\ServerException; -use App\Util\Nickname; use Component\Subscription\Controller\Subscribers as SubscribersController; use Component\Subscription\Controller\Subscriptions as SubscriptionsController; @@ -50,9 +49,7 @@ class Subscription extends Component $r->connect(id: 'actor_subscribe_add', uri_path: '/actor/subscribe/{object_id<\d+>}', target: [SubscribersController::class, 'subscribersAdd']); $r->connect(id: 'actor_subscribe_remove', uri_path: '/actor/unsubscribe/{object_id<\d+>}', target: [SubscribersController::class, 'subscribersRemove']); $r->connect(id: 'actor_subscriptions_id', uri_path: '/actor/{id<\d+>}/subscriptions', target: [SubscriptionsController::class, 'subscriptionsByActorId']); - $r->connect(id: 'actor_subscriptions_nickname', uri_path: '/@{nickname<' . Nickname::DISPLAY_FMT . '>}/subscriptions', target: [SubscriptionsController::class, 'subscriptionsByActorNickname']); $r->connect(id: 'actor_subscribers_id', uri_path: '/actor/{id<\d+>}/subscribers', target: [SubscribersController::class, 'subscribersByActorId']); - $r->connect(id: 'actor_subscribers_nickname', uri_path: '/@{nickname<' . Nickname::DISPLAY_FMT . '>}/subscribers', target: [SubscribersController::class, 'subscribersByActorNickname']); return Event::next; } diff --git a/plugins/ActivityPub/ActivityPub.php b/plugins/ActivityPub/ActivityPub.php index ed75c6e6b2..d44d4d39b1 100644 --- a/plugins/ActivityPub/ActivityPub.php +++ b/plugins/ActivityPub/ActivityPub.php @@ -185,8 +185,12 @@ class ActivityPub extends Plugin return Event::next; } switch ($route) { - case 'actor_view_id': - case 'actor_view_nickname': + case 'person_actor_view_id': + case 'person_actor_view_nickname': + case 'group_actor_view_id': + case 'group_actor_view_nickname': + case 'bot_actor_view_id': + case 'bot_actor_view_nickname': $response = ActorResponse::handle($vars['actor']); break; case 'note_view': diff --git a/plugins/ActivityPub/Util/Model/Actor.php b/plugins/ActivityPub/Util/Model/Actor.php index 6f340e59a7..5cf2032da1 100644 --- a/plugins/ActivityPub/Util/Model/Actor.php +++ b/plugins/ActivityPub/Util/Model/Actor.php @@ -45,6 +45,7 @@ use App\Util\Exception\ServerException; use App\Util\Formatting; use App\Util\TemporaryFile; use Component\Avatar\Avatar; +use Component\Group\Entity\LocalGroup; use DateTime; use DateTimeInterface; use Exception; @@ -198,7 +199,7 @@ class Actor extends Model $uri = $object->getUri(Router::ABSOLUTE_URL); $attr = [ '@context' => 'https://www.w3.org/ns/activitystreams', - 'type' => self::$_gs_actor_type_to_as2_actor_type[$object->getType()], + 'type' => ($object->getType() === GSActor::GROUP) ? (LocalGroup::getByPK(['actor_id' => $object->getId()])->getType() === 'organisation' ? 'Organization' : 'Group'): self::$_gs_actor_type_to_as2_actor_type[$object->getType()], 'id' => $uri, 'inbox' => Router::url('activitypub_actor_inbox', ['gsactor_id' => $object->getId()], Router::ABSOLUTE_URL), 'outbox' => Router::url('activitypub_actor_outbox', ['gsactor_id' => $object->getId()], Router::ABSOLUTE_URL), diff --git a/src/Controller/ActorFeed.php b/src/Controller/ActorFeed.php index 9dcd7ffe00..c1eace35c3 100644 --- a/src/Controller/ActorFeed.php +++ b/src/Controller/ActorFeed.php @@ -23,36 +23,23 @@ declare(strict_types = 1); namespace App\Controller; -use App\Entity as E; -use Component\Collection\Util\ActorControllerTrait; +use App\Core\Router\Router; +use App\Entity\Actor; use Component\Collection\Util\Controller\FeedController; +use InvalidArgumentException; +use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; class ActorFeed extends FeedController { - use ActorControllerTrait; - public function actorViewId(Request $request, int $id) + public function actorViewId(Request $request, int $id): RedirectResponse { - return $this->handleActorById( - $id, - fn ($actor) => [ - '_template' => 'actor/view.html.twig', - 'actor' => $actor, - 'nickname' => $actor->getNickname(), - ], - ); - } - - public function actorViewNickname(Request $request, string $nickname) - { - return $this->handleActorByNickname( - $nickname, - fn ($actor) => [ - '_template' => 'actor/view.html.twig', - 'actor' => $actor, - 'nickname' => $actor->getNickname(), - 'notes' => E\Note::getAllNotesByActor($actor), - ], - ); + $route_id = match (Actor::getById($id)->getType()) { + Actor::PERSON => 'person_actor_view_id', + Actor::GROUP => 'group_actor_view_id', + Actor::BOT => 'bot_actor_view_id', + default => throw new InvalidArgumentException(), + }; + return new RedirectResponse(Router::url($route_id, ['id' => $id]), status: 302); } } diff --git a/src/Entity/Actor.php b/src/Entity/Actor.php index 9f362696b4..28c71e437c 100644 --- a/src/Entity/Actor.php +++ b/src/Entity/Actor.php @@ -366,20 +366,12 @@ class Actor extends Entity public function getSubscriptionsUrl(): string { - if ($this->getIsLocal()) { - return Router::url('actor_subscriptions_nickname', ['nickname' => $this->getNickname()]); - } else { - return Router::url('actor_subscriptions_id', ['id' => $this->getId()]); - } + return Router::url('actor_subscriptions_id', ['id' => $this->getId()]); } public function getSubscribersUrl(): string { - if ($this->getIsLocal()) { - return Router::url('actor_subscribers_nickname', ['nickname' => $this->getNickname()]); - } else { - return Router::url('actor_subscribers_id', ['id' => $this->getId()]); - } + return Router::url('actor_subscribers_id', ['id' => $this->getId()]); } /** @@ -445,7 +437,7 @@ class Actor extends Entity $url = null; if (Event::handle('StartGetActorUrl', [$this, $type, &$url]) === Event::next) { $url = match ($this->type) { - self::PERSON, self::BOT => Router::url('actor_view_nickname', ['nickname' => mb_strtolower($this->getNickname())], $type), + self::PERSON, self::BOT => Router::url('person_actor_view_nickname', ['nickname' => mb_strtolower($this->getNickname())], $type), self::GROUP => Router::url('group_actor_view_nickname', ['nickname' => $this->getNickname()], $type), default => throw new BugFoundException('Actor type added but `Actor::getUrl` was not updated'), }; diff --git a/src/Entity/Note.php b/src/Entity/Note.php index 25ace3174e..a16108b58c 100644 --- a/src/Entity/Note.php +++ b/src/Entity/Note.php @@ -293,7 +293,6 @@ class Note extends Entity public static function getAllNotesByActor(Actor $actor): array { - // TODO: Enforce scoping on the notes before returning return DB::findBy('note', ['actor_id' => $actor->getId()], order_by: ['created' => 'DESC', 'id' => 'DESC']); } diff --git a/src/Entity/RelatedGroup.php b/src/Entity/RelatedGroup.php deleted file mode 100644 index 56ffdfb482..0000000000 --- a/src/Entity/RelatedGroup.php +++ /dev/null @@ -1,96 +0,0 @@ -. -// }}} - -namespace App\Entity; - -use App\Core\Entity; -use DateTimeInterface; - -/** - * Entity for related groups - * - * @category DB - * @package GNUsocial - * - * @author Zach Copley - * @copyright 2010 StatusNet Inc. - * @author Mikael Nordfeldth - * @copyright 2009-2014 Free Software Foundation, Inc http://www.fsf.org - * @author Hugo Sales - * @copyright 2020-2021 Free Software Foundation, Inc http://www.fsf.org - * @license https://www.gnu.org/licenses/agpl.html GNU AGPL v3 or later - */ -class RelatedGroup extends Entity -{ - // {{{ Autocode - // @codeCoverageIgnoreStart - private int $group_id; - private int $related_group_id; - private \DateTimeInterface $created; - - public function setGroupId(int $group_id): self - { - $this->group_id = $group_id; - return $this; - } - - public function getGroupId(): int - { - return $this->group_id; - } - - public function setRelatedGroupId(int $related_group_id): self - { - $this->related_group_id = $related_group_id; - return $this; - } - - public function getRelatedGroupId(): int - { - return $this->related_group_id; - } - - public function setCreated(\DateTimeInterface $created): self - { - $this->created = $created; - return $this; - } - - public function getCreated(): \DateTimeInterface - { - return $this->created; - } - - // @codeCoverageIgnoreEnd - // }}} Autocode - - public static function schemaDef(): array - { - return [ - 'name' => 'related_group', - // @fixme description for related_group? - 'fields' => [ - 'group_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Group.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'foreign key to group'], - 'related_group_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Group.id', 'multiplicity' => 'one to one', 'not null' => true, 'description' => 'foreign key to group'], - 'created' => ['type' => 'datetime', 'not null' => true, 'default' => 'CURRENT_TIMESTAMP', 'description' => 'date this record was created'], - ], - 'primary key' => ['group_id', 'related_group_id'], - ]; - } -} diff --git a/src/Routes/Actor.php b/src/Routes/Actor.php index 638e707f5d..fdffa85d32 100644 --- a/src/Routes/Actor.php +++ b/src/Routes/Actor.php @@ -37,7 +37,6 @@ namespace App\Routes; use App\Controller as C; use App\Core\Router\RouteLoader; -use App\Util\Nickname; abstract class Actor { @@ -46,6 +45,5 @@ abstract class Actor public static function load(RouteLoader $r): void { $r->connect(id: 'actor_view_id', uri_path: '/actor/{id<\d+>}', target: [C\ActorFeed::class, 'actorViewId']); - $r->connect(id: 'actor_view_nickname', uri_path: '/@{nickname<' . Nickname::DISPLAY_FMT . '>}', target: [C\ActorFeed::class, 'actorViewNickname'], options: ['is_system_path' => false]); } } diff --git a/src/Routes/Main.php b/src/Routes/Main.php index 6291932d65..532b00ad54 100644 --- a/src/Routes/Main.php +++ b/src/Routes/Main.php @@ -69,7 +69,5 @@ abstract class Main foreach (['privacy', 'tos', 'version', 'source'] as $s) { $r->connect('doc_' . $s, '/doc/' . $s, C\TemplateController::class, ['template' => 'doc/' . $s . '.html.twig']); } - - $r->connect('settings', '/settings', [C\UserPanel::class, 'allSettings']); } } diff --git a/templates/cards/blocks/navigation.html.twig b/templates/cards/blocks/navigation.html.twig index bab1332d34..bbec5ae9b7 100644 --- a/templates/cards/blocks/navigation.html.twig +++ b/templates/cards/blocks/navigation.html.twig @@ -58,7 +58,7 @@ {% block profile_current_actor %}