[CONTROLLER][ENTITY][Actor] Add way of creating a group that doesn't exist

This commit is contained in:
Hugo Sales 2021-12-21 12:12:03 +00:00
parent 88a137fb15
commit fa863d9e03
No known key found for this signature in database
GPG Key ID: 7D0C7EAFC9D835A0
5 changed files with 176 additions and 26 deletions

View File

@ -26,23 +26,26 @@ namespace App\Controller;
use App\Core\Cache; use App\Core\Cache;
use App\Core\Controller\ActorController; use App\Core\Controller\ActorController;
use App\Core\DB\DB; use App\Core\DB\DB;
use App\Core\Router\Router; use App\Core\Form;
use Symfony\Component\HttpFoundation\RedirectResponse;
use function App\Core\I18n\_m; 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; use Symfony\Component\HttpFoundation\Request;
class Actor extends ActorController class Actor extends ActorController
{ {
public function actorViewId(Request $request, int $id) public function actorViewId(Request $request, int $id)
{ {
return $this->handleActorById( return $this->handleActorById(
$id, $id,
fn ($actor) => [ fn ($actor) => [
'_template' => 'actor/view.html.twig', '_template' => 'actor/view.html.twig',
'actor' => $actor 'actor' => $actor,
] ],
); );
} }
@ -52,21 +55,80 @@ class Actor extends ActorController
$nickname, $nickname,
fn ($actor) => [ fn ($actor) => [
'_template' => 'actor/view.html.twig', '_template' => 'actor/view.html.twig',
'actor' => $actor, 'actor' => $actor,
'notes' => \App\Entity\Note::getAllNotesByActor($actor) 'notes' => \App\Entity\Note::getAllNotesByActor($actor),
] ],
); );
} }
/** public function groupViewId(Request $request, int $id)
* The page where the actor's info is shown
*/
public function ActorShowId(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(),
];
} }
} }

View File

@ -29,8 +29,10 @@ use App\Core\Entity;
use App\Core\Event; use App\Core\Event;
use App\Core\Router\Router; use App\Core\Router\Router;
use App\Core\UserRoles; use App\Core\UserRoles;
use App\Util\Exception\DuplicateFoundException;
use App\Util\Exception\NicknameException; use App\Util\Exception\NicknameException;
use App\Util\Exception\NotFoundException; use App\Util\Exception\NotFoundException;
use App\Util\Formatting;
use App\Util\Nickname; use App\Util\Nickname;
use Component\Avatar\Avatar; use Component\Avatar\Avatar;
use Component\Tag\Tag as TagComponent; use Component\Tag\Tag as TagComponent;
@ -261,7 +263,7 @@ class Actor extends Entity
]; ];
} }
public function getLocalUser() public function getLocalUser(): ?LocalUser
{ {
if ($this->getIsLocal()) { if ($this->getIsLocal()) {
return DB::findOneBy('local_user', ['id' => $this->getId()]); 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 try {
return false; 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') public function getAvatarUrl(string $size = 'full')
@ -467,7 +492,16 @@ class Actor extends Entity
{ {
$uri = null; $uri = null;
if (Event::handle('StartGetActorUri', [$this, $type, &$uri]) === Event::next) { 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]); Event::handle('EndGetActorUri', [$this, $type, &$uri]);
} }
return $uri; return $uri;
@ -478,7 +512,16 @@ class Actor extends Entity
$url = null; $url = null;
if (Event::handle('StartGetActorUrl', [$this, $type, &$url]) === Event::next) { if (Event::handle('StartGetActorUrl', [$this, $type, &$url]) === Event::next) {
if ($this->getIsLocal()) { 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 { } else {
return $this->getUri($type); return $this->getUri($type);
} }

View File

@ -1,5 +1,7 @@
<?php <?php
declare(strict_types = 1);
// {{{ License // {{{ License
// This file is part of GNU social - https://www.gnu.org/software/social // This file is part of GNU social - https://www.gnu.org/software/social
// //
@ -44,8 +46,8 @@ class GroupMember extends Entity
private int $actor_id; private int $actor_id;
private ?bool $is_admin; private ?bool $is_admin;
private ?string $uri; private ?string $uri;
private \DateTimeInterface $created; private DateTimeInterface $created;
private \DateTimeInterface $modified; private DateTimeInterface $modified;
public function setGroupId(int $group_id): self public function setGroupId(int $group_id): self
{ {
@ -121,7 +123,7 @@ class GroupMember extends Entity
return [ return [
'name' => 'group_member', 'name' => 'group_member',
'fields' => [ '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'], '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?'], 'is_admin' => ['type' => 'bool', 'default' => false, 'description' => 'is this actor an admin?'],
'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'], 'uri' => ['type' => 'varchar', 'length' => 191, 'description' => 'universal identifier'],

View File

@ -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() }}
<link rel="stylesheet" href="{{ asset('assets/default_theme/css/pages/feeds.css') }}" type="text/css">
{% 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 %}
<main class="feed" tabindex="0" role="feed">
<div class="h-feed hfeed notes">
{% if notes is defined and notes is not empty %}
{% for note in notes %}
{% block current_note %}
{{ noteView.macro_note(note, null) }}
<hr tabindex="0" title="{{ 'End of note and replies.' | trans }}">
{% endblock current_note %}
{% endfor %}
{% else %}
<div id="empty-notes"><h1>{% trans %}No notes here.{% endtrans %}</h1></div>
{% endif %}
</div>
</main>
{% else %}
<div class="section-widget-padded section-widget">
<p>{% trans with { '%group%': nickname } %}The group <em>%group%</em> doesn't exist. Would you like to create it?{% endtrans %}</p>
{{ form(create_form) }}
</div>
{% endif %}
{% endblock body %}

View File

@ -1,7 +1,9 @@
{% extends 'stdgrid.html.twig' %} {% extends 'stdgrid.html.twig' %}
{% import '/cards/note/view.html.twig' as noteView %} {% import '/cards/note/view.html.twig' as noteView %}
{% block title %}{{ actor.getNickname() ~ '\'s profile' | trans }}{% endblock %} {% set nickname = nickname|escape %}
{% block title %}{% trans with {'%nick%': actor.getNickname()} %}%nick%'s profile{% endtrans %}{% endblock %}y
{% block stylesheets %} {% block stylesheets %}
{{ parent() }} {{ parent() }}