+ {% endblock current_note %} + {% endfor %} + {% else %} +
diff --git a/src/Controller/Actor.php b/src/Controller/Actor.php
index 07ac511f2a..d9a8c0ee54 100644
--- a/src/Controller/Actor.php
+++ b/src/Controller/Actor.php
@@ -26,23 +26,26 @@ namespace App\Controller;
use App\Core\Cache;
use App\Core\Controller\ActorController;
use App\Core\DB\DB;
-use App\Core\Router\Router;
-use Symfony\Component\HttpFoundation\RedirectResponse;
+use App\Core\Form;
use function App\Core\I18n\_m;
-use App\Util\Exception\ClientException;
+use App\Core\Log;
+use App\Entity as E;
+use App\Util\Common;
+use App\Util\Exception\RedirectException;
+use App\Util\Nickname;
+use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\HttpFoundation\Request;
class Actor extends ActorController
{
-
public function actorViewId(Request $request, int $id)
{
return $this->handleActorById(
$id,
fn ($actor) => [
'_template' => 'actor/view.html.twig',
- 'actor' => $actor
- ]
+ 'actor' => $actor,
+ ],
);
}
@@ -52,21 +55,80 @@ class Actor extends ActorController
$nickname,
fn ($actor) => [
'_template' => 'actor/view.html.twig',
- 'actor' => $actor,
- 'notes' => \App\Entity\Note::getAllNotesByActor($actor)
- ]
+ 'actor' => $actor,
+ 'notes' => \App\Entity\Note::getAllNotesByActor($actor),
+ ],
);
}
- /**
- * The page where the actor's info is shown
- */
- public function ActorShowId(Request $request, int $id)
+ public function groupViewId(Request $request, int $id)
{
- return $this->ActorById($id, fn ($actor) => ['_template' => 'actor/view.html.twig', 'actor' => $actor]);
+ return $this->handleActorById(
+ $id,
+ fn ($actor) => [
+ '_template' => 'actor/group_view.html.twig',
+ 'actor' => $actor,
+ ],
+ );
}
- public function ActorShowNickname(Request $request, string $nickname)
+
+ public function groupViewNickname(Request $request, string $nickname)
{
- return $this->ActorByNickname($nickname, fn ($actor) => ['_template' => 'actor/view.html.twig', 'actor' => $actor, 'notes' => \App\Entity\Note::getAllNotesByActor($actor)]);
+ Nickname::validate($nickname, which: Nickname::CHECK_LOCAL_GROUP); // throws
+ $group = E\Actor::getByNickname($nickname, type: E\Actor::GROUP);
+ if (\is_null($group)) {
+ $actor = Common::actor();
+ if (!\is_null($actor)) {
+ $form = Form::create([
+ ['create', SubmitType::class, ['label' => _m('Create this group')]],
+ ]);
+
+ $form->handleRequest($request);
+ if ($form->isSubmitted() && $form->isValid()) {
+ Log::info(
+ _m(
+ 'Actor id:{actor_id} nick:{actor_nick} created the group {nickname}',
+ ['{actor_id}' => $actor->getId(), 'actor_nick' => $actor->getNickname(), 'nickname' => $nickname],
+ ),
+ );
+
+ $group = E\Actor::create([
+ 'nickname' => $nickname,
+ 'type' => E\Actor::GROUP,
+ 'is_local' => true,
+ ]);
+ DB::persist($group);
+ DB::persist(E\Subscription::create([
+ 'subscriber' => $group->getId(),
+ 'subscribed' => $group->getId(),
+ ]));
+ DB::persist(E\Subscription::create([
+ 'subscriber' => $actor->getId(),
+ 'subscribed' => $group->getId(),
+ ]));
+ DB::persist(E\GroupMember::create([
+ 'group_id' => $group->getId(),
+ 'actor_id' => $actor->getId(),
+ 'is_admin' => true,
+ ]));
+ DB::flush();
+ Cache::delete(self::cacheKeys($actor->getId())['subscriber']);
+ Cache::delete(self::cacheKeys($actor->getId())['subscribed']);
+ throw new RedirectException;
+ }
+
+ return [
+ '_template' => 'actor/group_view.html.twig',
+ 'nickname' => $nickname,
+ 'create_form' => $form->createView(),
+ ];
+ }
+ }
+
+ return [
+ '_template' => 'actor/group_view.html.twig',
+ 'actor' => $group,
+ 'nickname' => $group->getNickname(),
+ ];
}
}
diff --git a/src/Entity/Actor.php b/src/Entity/Actor.php
index e1d16ddd2a..9d691212bc 100644
--- a/src/Entity/Actor.php
+++ b/src/Entity/Actor.php
@@ -29,8 +29,10 @@ use App\Core\Entity;
use App\Core\Event;
use App\Core\Router\Router;
use App\Core\UserRoles;
+use App\Util\Exception\DuplicateFoundException;
use App\Util\Exception\NicknameException;
use App\Util\Exception\NotFoundException;
+use App\Util\Formatting;
use App\Util\Nickname;
use Component\Avatar\Avatar;
use Component\Tag\Tag as TagComponent;
@@ -261,7 +263,7 @@ class Actor extends Entity
];
}
- public function getLocalUser()
+ public function getLocalUser(): ?LocalUser
{
if ($this->getIsLocal()) {
return DB::findOneBy('local_user', ['id' => $this->getId()]);
@@ -270,10 +272,33 @@ class Actor extends Entity
}
}
- public function isGroup()
+ /**
+ * @return ?self
+ */
+ public static function getByNickname(string $nickname, int $type = self::PERSON): ?self
{
- // TODO: implement
- return false;
+ try {
+ return DB::findOneBy(self::class, ['nickname' => $nickname, 'type' => $type]);
+ } catch (NotFoundException) {
+ return null;
+ } catch (DuplicateFoundException $e) {
+ throw new BugFoundException("Multiple actors with the same nickname '{$nickname}' found", previous: $e);
+ }
+ }
+
+ public function __call(string $name, array $arguments): mixed
+ {
+ if (Formatting::startsWith($name, 'is')) {
+ $type = Formatting::removePrefix($name, 'is');
+ $const = self::class . '::' . mb_strtoupper($type);
+ if (\defined($const)) {
+ return $this->type === \constant($const);
+ } else {
+ throw new BugFoundException("Actor cannot be a '{$type}', check your spelling");
+ }
+ } else {
+ return parent::__call($name, $arguments);
+ }
}
public function getAvatarUrl(string $size = 'full')
@@ -467,7 +492,16 @@ class Actor extends Entity
{
$uri = null;
if (Event::handle('StartGetActorUri', [$this, $type, &$uri]) === Event::next) {
- $uri = Router::url('actor_view_id', ['id' => $this->getId()], $type);
+ switch ($this->type) {
+ case self::PERSON:
+ case self::ORGANIZATION:
+ case self::BUSINESS:
+ case self::BOT:
+ $uri = Router::url('actor_view_id', ['id' => $this->getId()], $type);
+ break;
+ case self::GROUP:
+ $uri = Router::url('group_actor_view_id', ['id' => $this->getId()], $type);
+ }
Event::handle('EndGetActorUri', [$this, $type, &$uri]);
}
return $uri;
@@ -478,7 +512,16 @@ class Actor extends Entity
$url = null;
if (Event::handle('StartGetActorUrl', [$this, $type, &$url]) === Event::next) {
if ($this->getIsLocal()) {
- $url = Router::url('actor_view_nickname', ['nickname' => $this->getNickname()], $type);
+ switch ($this->type) {
+ case self::PERSON:
+ case self::ORGANIZATION:
+ case self::BUSINESS:
+ case self::BOT:
+ $url = Router::url('actor_view_nickname', ['nickname' => $this->getNickname()], $type);
+ break;
+ case self::GROUP:
+ $url = Router::url('group_actor_view_nickname', ['nickname' => $this->getNickname()], $type);
+ }
} else {
return $this->getUri($type);
}
diff --git a/src/Entity/GroupMember.php b/src/Entity/GroupMember.php
index 59da000354..f96bb8090f 100644
--- a/src/Entity/GroupMember.php
+++ b/src/Entity/GroupMember.php
@@ -1,5 +1,7 @@
'group_member',
'fields' => [
- 'group_id' => ['type' => 'int', 'foreign key' => true, 'target' => 'Group.id', 'multiplicity' => 'one to one', 'name' => 'group_member_group_id_fkey', 'not null' => true, 'description' => 'foreign key to group table'],
+ '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'],
diff --git a/templates/actor/group_view.html.twig b/templates/actor/group_view.html.twig
new file mode 100644
index 0000000000..fc41bb84df
--- /dev/null
+++ b/templates/actor/group_view.html.twig
@@ -0,0 +1,41 @@
+{% extends 'stdgrid.html.twig' %}
+{% import '/cards/note/view.html.twig' as noteView %}
+
+{% set nickname = nickname|escape %}
+
+{% block title %}{{ nickname }}{% endblock %}
+
+{% block stylesheets %}
+ {{ parent() }}
+
+{% endblock stylesheets %}
+
+{% block body %}
+
+ {% if actor is defined and actor is not null %}
+ {% block profile_view %}
+ {% include 'cards/profile/view.html.twig' %}
+ {% endblock profile_view %}
+
+
+ {% endblock current_note %}
+ {% endfor %}
+ {% else %}
+ {% trans %}No notes here.{% endtrans %}