forked from GNUsocial/gnu-social
		
	[COMPONENTS][Group] Create a group route added, template polished
[COMPONENTS][Circle] Removed any Group related route from shouldAddToRightPanel event [CARDS][Profile] Block should now allow inline long nicknames to not break
This commit is contained in:
		| @@ -192,7 +192,7 @@ class Circle extends Component | ||||
|      */ | ||||
|     protected function shouldAddToRightPanel(Actor $user, $vars, Request $request): bool | ||||
|     { | ||||
|         return \in_array($vars['path'], ['actor_view_nickname', 'actor_view_id', 'group_actor_view_nickname', 'group_actor_view_id']); | ||||
|         return \in_array($vars['path'], ['actor_view_nickname', 'actor_view_id']); | ||||
|     } | ||||
|  | ||||
|     protected function getCollectionsBy(Actor $owner, ?array $vars = null, bool $ids_only = false): array | ||||
|   | ||||
| @@ -32,7 +32,14 @@ use App\Core\UserRoles; | ||||
| use App\Entity as E; | ||||
| use App\Util\Common; | ||||
| use App\Util\Exception\ClientException; | ||||
| 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\ActorForms; | ||||
| use App\Util\Nickname; | ||||
| use Component\Collection\Util\ActorControllerTrait; | ||||
| @@ -41,11 +48,22 @@ use Component\Group\Entity\GroupMember; | ||||
| use Component\Group\Entity\LocalGroup; | ||||
| use Component\Subscription\Entity\ActorSubscription; | ||||
| use Symfony\Component\Form\Extension\Core\Type\SubmitType; | ||||
| use Symfony\Component\Form\Extension\Core\Type\TextType; | ||||
| use Symfony\Component\HttpFoundation\Request; | ||||
|  | ||||
| class Group extends FeedController | ||||
| { | ||||
|     use ActorControllerTrait; | ||||
|  | ||||
|     /** | ||||
|      * View a group providing its id | ||||
|      * | ||||
|      * @param int $id The id of the group to be shown | ||||
|      * | ||||
|      * @throws ClientException | ||||
|      * | ||||
|      * @return array Containing both the template to be used and the group actor | ||||
|      */ | ||||
|     public function groupViewId(Request $request, int $id) | ||||
|     { | ||||
|         return $this->handleActorById( | ||||
| @@ -58,14 +76,15 @@ class Group extends FeedController | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * View a group feed and give the option of creating it if it doesn't exist | ||||
|      * View a group feed by its nickname | ||||
|      * | ||||
|      * @throws \App\Util\Exception\NicknameEmptyException | ||||
|      * @throws \App\Util\Exception\NicknameNotAllowedException | ||||
|      * @throws \App\Util\Exception\NicknameTakenException | ||||
|      * @throws \App\Util\Exception\NicknameTooLongException | ||||
|      * @throws \App\Util\Exception\ServerException | ||||
|      * @throws RedirectException | ||||
|      * @param string $nickname The group's nickname to be shown | ||||
|      * | ||||
|      * @throws NicknameEmptyException | ||||
|      * @throws NicknameNotAllowedException | ||||
|      * @throws NicknameTakenException | ||||
|      * @throws NicknameTooLongException | ||||
|      * @throws ServerException | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
| @@ -76,74 +95,27 @@ class Group extends FeedController | ||||
|         $actor          = Common::actor(); | ||||
|         $subscribe_form = null; | ||||
|  | ||||
|         if (\is_null($group)) { | ||||
|             if (!\is_null($actor)) { | ||||
|                 $create_form = Form::create([ | ||||
|                     ['create', SubmitType::class, ['label' => _m('Create this group')]], | ||||
|                 ]); | ||||
|  | ||||
|                 $create_form->handleRequest($request); | ||||
|                 if ($create_form->isSubmitted() && $create_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], | ||||
|                         ), | ||||
|                     ); | ||||
|  | ||||
|                     DB::persist($group = E\Actor::create([ | ||||
|                         'nickname' => $nickname, | ||||
|                         'type'     => E\Actor::GROUP, | ||||
|                         'is_local' => true, | ||||
|                         'roles'    => UserRoles::BOT, | ||||
|                     ])); | ||||
|                     DB::persist(LocalGroup::create([ | ||||
|                         'group_id' => $group->getId(), | ||||
|                         'nickname' => $nickname, | ||||
|                     ])); | ||||
|                     DB::persist(ActorSubscription::create([ | ||||
|                         'subscriber' => $group->getId(), | ||||
|                         'subscribed' => $group->getId(), | ||||
|                     ])); | ||||
|                     DB::persist(GroupMember::create([ | ||||
|                         'group_id' => $group->getId(), | ||||
|                         'actor_id' => $actor->getId(), | ||||
|                         'is_admin' => true, | ||||
|                     ])); | ||||
|                     DB::flush(); | ||||
|                     Cache::delete(E\Actor::cacheKeys($actor->getId())['subscriber']); | ||||
|                     Cache::delete(E\Actor::cacheKeys($actor->getId())['subscribed']); | ||||
|                     throw new RedirectException(); | ||||
|                 } | ||||
|  | ||||
|                 return [ | ||||
|                     '_template'   => 'group/view.html.twig', | ||||
|                     'nickname'    => $nickname, | ||||
|                     'create_form' => $create_form->createView(), | ||||
|                 ]; | ||||
|             } | ||||
|         } else { | ||||
|             if (!\is_null($actor) | ||||
|                 && \is_null(Cache::get( | ||||
|                     ActorSubscription::cacheKeys($actor, $group)['subscribed'], | ||||
|                     fn () => DB::findOneBy('subscription', [ | ||||
|                         'subscriber' => $actor->getId(), | ||||
|                         'subscribed' => $group->getId(), | ||||
|                     ], return_null: true), | ||||
|                 )) | ||||
|             ) { | ||||
|                 $subscribe_form = Form::create([['subscribe', SubmitType::class, ['label' => _m('Subscribe to this group')]]]); | ||||
|                 $subscribe_form->handleRequest($request); | ||||
|                 if ($subscribe_form->isSubmitted() && $subscribe_form->isValid()) { | ||||
|                     DB::persist(ActorSubscription::create([ | ||||
|                         'subscriber' => $actor->getId(), | ||||
|                         'subscribed' => $group->getId(), | ||||
|                     ])); | ||||
|                     DB::flush(); | ||||
|                     Cache::delete(E\Actor::cacheKeys($group->getId())['subscriber']); | ||||
|                     Cache::delete(E\Actor::cacheKeys($actor->getId())['subscribed']); | ||||
|                     Cache::delete(ActorSubscription::cacheKeys($actor, $group)['subscribed']); | ||||
|                 } | ||||
|         if (!\is_null($group) | ||||
|             && !\is_null($actor) | ||||
|             && \is_null(Cache::get( | ||||
|                 ActorSubscription::cacheKeys($actor, $group)['subscribed'], | ||||
|                 fn () => DB::findOneBy('actor_subscription', [ | ||||
|                     'subscriber_id' => $actor->getId(), | ||||
|                     'subscribed_id' => $group->getId(), | ||||
|                 ], return_null: true), | ||||
|             )) | ||||
|         ) { | ||||
|             $subscribe_form = Form::create([['subscribe', SubmitType::class, ['label' => _m('Subscribe to this group')]]]); | ||||
|             $subscribe_form->handleRequest($request); | ||||
|             if ($subscribe_form->isSubmitted() && $subscribe_form->isValid()) { | ||||
|                 DB::persist(ActorSubscription::create([ | ||||
|                     'subscriber_id' => $actor->getId(), | ||||
|                     'subscribed_id' => $group->getId(), | ||||
|                 ])); | ||||
|                 DB::flush(); | ||||
|                 Cache::delete(E\Actor::cacheKeys($group->getId())['subscribers']); | ||||
|                 Cache::delete(E\Actor::cacheKeys($actor->getId())['subscribed']); | ||||
|                 Cache::delete(ActorSubscription::cacheKeys($actor, $group)['subscribed']); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -167,6 +139,83 @@ class Group extends FeedController | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Page that allows an actor to create a new group | ||||
|      * | ||||
|      * @throws RedirectException | ||||
|      * @throws ServerException | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function groupCreate(Request $request) | ||||
|     { | ||||
|         if (\is_null($actor = Common::actor())) { | ||||
|             throw new RedirectException('security_login'); | ||||
|         } | ||||
|  | ||||
|         $create_form = Form::create([ | ||||
|             ['group_nickname', TextType::class, ['label' => _m('Group nickname')]], | ||||
|             ['group_create', SubmitType::class, ['label' => _m('Create this group!')]], | ||||
|         ]); | ||||
|  | ||||
|         $create_form->handleRequest($request); | ||||
|         if ($create_form->isSubmitted() && $create_form->isValid()) { | ||||
|             $data     = $create_form->getData(); | ||||
|             $nickname = $data['group_nickname']; | ||||
|  | ||||
|             Log::info( | ||||
|                 _m( | ||||
|                     'Actor id:{actor_id} nick:{actor_nick} created the group {nickname}', | ||||
|                     ['{actor_id}' => $actor->getId(), 'actor_nick' => $actor->getNickname(), 'nickname' => $nickname], | ||||
|                 ), | ||||
|             ); | ||||
|  | ||||
|             DB::persist($group = E\Actor::create([ | ||||
|                 'nickname' => $nickname, | ||||
|                 'type'     => E\Actor::GROUP, | ||||
|                 'is_local' => true, | ||||
|                 'roles'    => UserRoles::BOT, | ||||
|             ])); | ||||
|             DB::persist(LocalGroup::create([ | ||||
|                 'group_id' => $group->getId(), | ||||
|                 'nickname' => $nickname, | ||||
|             ])); | ||||
|             DB::persist(ActorSubscription::create([ | ||||
|                 'subscriber_id' => $group->getId(), | ||||
|                 'subscribed_id' => $group->getId(), | ||||
|             ])); | ||||
|             DB::persist(GroupMember::create([ | ||||
|                 'group_id' => $group->getId(), | ||||
|                 'actor_id' => $actor->getId(), | ||||
|                 'is_admin' => true, | ||||
|             ])); | ||||
|             DB::flush(); | ||||
|             Cache::delete(E\Actor::cacheKeys($actor->getId())['subscribers']); | ||||
|             Cache::delete(E\Actor::cacheKeys($actor->getId())['subscribed']); | ||||
|  | ||||
|             throw new RedirectException(); | ||||
|         } | ||||
|  | ||||
|         return [ | ||||
|             '_template'   => 'group/create.html.twig', | ||||
|             'create_form' => $create_form->createView(), | ||||
|         ]; | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Settings page for the group with the provided nickname, checks if the current actor can administrate given group | ||||
|      * | ||||
|      * @throws ClientException | ||||
|      * @throws NicknameEmptyException | ||||
|      * @throws NicknameInvalidException | ||||
|      * @throws NicknameNotAllowedException | ||||
|      * @throws NicknameTakenException | ||||
|      * @throws NicknameTooLongException | ||||
|      * @throws NoLoggedInUser | ||||
|      * @throws ServerException | ||||
|      * | ||||
|      * @return array | ||||
|      */ | ||||
|     public function groupSettings(Request $request, string $nickname) | ||||
|     { | ||||
|         $group = LocalGroup::getActorByNickname($nickname); | ||||
|   | ||||
| @@ -39,6 +39,7 @@ 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_actor_view_nickname', uri_path: '/!{nickname<' . Nickname::DISPLAY_FMT . '>}', target: [C\Group::class, 'groupViewNickname'], options: ['is_system_path' => false]); | ||||
|         $r->connect(id: 'group_settings', uri_path: '/!{nickname<' . Nickname::DISPLAY_FMT . '>}/settings', target: [C\Group::class, 'groupSettings'], options: ['is_system_path' => false]); | ||||
| @@ -54,7 +55,7 @@ class Group extends Component | ||||
|         $group = $vars['actor']; | ||||
|         if (!\is_null($actor) && $group->isGroup() && $actor->canAdmin($group)) { | ||||
|             $url   = Router::url('group_settings', ['nickname' => $group->getNickname()]); | ||||
|             $res[] = HTML::html(['hr' => '', 'a' => ['attrs' => ['href' => $url, 'title' => _m('Edit group settings')], 'p' => _m('Group settings')]]); | ||||
|             $res[] = HTML::html(['a' => ['attrs' => ['href' => $url, 'title' => _m('Edit group settings'), 'class' => 'profile-extra-actions'], _m('Group settings')]]); | ||||
|         } | ||||
|         return Event::next; | ||||
|     } | ||||
|   | ||||
							
								
								
									
										5
									
								
								components/Group/templates/group/create.html.twig
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								components/Group/templates/group/create.html.twig
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,5 @@ | ||||
| {% extends 'stdgrid.html.twig' %} | ||||
|  | ||||
| {% block body %} | ||||
|     {{ form(create_form) }} | ||||
| {% endblock body %} | ||||
| @@ -10,41 +10,56 @@ | ||||
| {% endblock stylesheets %} | ||||
|  | ||||
| {% block body %} | ||||
|     {% if subscribe_form is defined and subscribe_form is not null %} | ||||
|         {{ form(subscribe_form) }} | ||||
|     {% endif %} | ||||
|  | ||||
|     {% if actor is defined and actor is not null %} | ||||
|         {% block profile_view %} | ||||
|             {% include 'cards/profile/view.html.twig' with { 'actor': actor } only %} | ||||
|         {% 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 conversation in notes %} | ||||
|                         {% block current_note %} | ||||
|                             {% if conversation is instanceof('array') %} | ||||
|                                 {{ noteView.macro_note(conversation['note'], conversation['replies']) }} | ||||
|                             {% else %} | ||||
|                                 {{ noteView.macro_note(conversation) }} | ||||
|                             {% endif %} | ||||
|                             <hr tabindex="0" title="{{ 'End of note and replies.' | trans }}"> | ||||
|                         {% endblock current_note %} | ||||
|                     {% endfor %} | ||||
|         {% if notes is defined %} | ||||
|             <article> | ||||
|                 <header class="feed-header"> | ||||
|                     {% if page_title is defined %} | ||||
|                         <h1 class="heading-no-margin">{{ page_title | trans }}</h1> | ||||
|                     {% else %} | ||||
|                         <h1 class="heading-no-margin">{{ 'Notes' | trans }}</h1> | ||||
|                     {% endif %} | ||||
|                     <nav class="feed-actions"> | ||||
|                         <details class="feed-actions-details"> | ||||
|                             <summary> | ||||
|                                 {{ icon('filter', 'icon icon-feed-actions') | raw }} {# button-container #} | ||||
|                             </summary> | ||||
|                             <div class="feed-actions-details-dropdown"> | ||||
|                                 <menu> | ||||
|                                     {% for block in handle_event('AddFeedActions', app.request, notes is defined and notes is not empty) %} | ||||
|                                         {{ block | raw }} | ||||
|                                     {% endfor %} | ||||
|                                 </menu> | ||||
|                             </div> | ||||
|                         </details> | ||||
|                     </nav> | ||||
|                 </header> | ||||
|  | ||||
|                 {% if notes is not empty %} | ||||
|                     {# Backwards compatibility with hAtom 0.1 #} | ||||
|                     <section class="feed h-feed hfeed notes" tabindex="0" role="feed"> | ||||
|                         {% for conversation in notes %} | ||||
|                             {% block current_note %} | ||||
|                                 {% if conversation is instanceof('array') %} | ||||
|                                     {{ noteView.macro_note(conversation['note'], conversation['replies']) }} | ||||
|                                 {% else %} | ||||
|                                     {{ noteView.macro_note(conversation) }} | ||||
|                                 {% endif %} | ||||
|                                 <hr tabindex="0" title="{{ 'End of note and replies.' | trans }}"> | ||||
|                             {% endblock current_note %} | ||||
|                         {% endfor %} | ||||
|                     </section> | ||||
|                 {% else %} | ||||
|                     <div id="empty-notes"><h1>{% trans %}No notes here.{% endtrans %}</h1></div> | ||||
|                     <section class="feed h-feed hfeed notes" tabindex="0" role="feed"> | ||||
|                         <strong>{% trans %}No notes yet...{% endtrans %}</strong> | ||||
|                     </section> | ||||
|                 {% endif %} | ||||
|             </div> | ||||
|         </main> | ||||
|          | ||||
|     {% else %} | ||||
|         <div class="section-padding section-widget"> | ||||
|             <p>{% trans with { '%group%': nickname } %}The group <em>%group%</em> doesn't exist.{% endtrans %}</p> | ||||
|             {% if create_form is defined and create_form is not null %} | ||||
|                 <p>{% trans %}Would you like to create it?{% endtrans %}</p> | ||||
|                 {{ form(create_form) }} | ||||
|             {% endif %} | ||||
|         </div> | ||||
|             </article> | ||||
|         {% endif %} | ||||
|     {% endif %} | ||||
| {% endblock body %} | ||||
|   | ||||
| @@ -16,6 +16,11 @@ | ||||
|  | ||||
| .profile-info { | ||||
|     display: flex; | ||||
|     flex-wrap: wrap; | ||||
| } | ||||
|  | ||||
| .profile-info .avatar { | ||||
|     flex: 0.5; | ||||
| } | ||||
|  | ||||
| .profile-info-url { | ||||
| @@ -34,6 +39,10 @@ | ||||
|     display: block; | ||||
| } | ||||
|  | ||||
| .profile-info section { | ||||
|     flex: 1; | ||||
| } | ||||
|  | ||||
| .profile-stats { | ||||
|     align-self: center; | ||||
|     margin-left: auto; | ||||
| @@ -65,6 +74,17 @@ | ||||
|     margin: 4px unset unset; | ||||
| } | ||||
|  | ||||
| .profile-extra-actions { | ||||
|     margin-top: var(--s); | ||||
|     margin-right: var(--s); | ||||
|     background: var(--gradient), var(--background-hard); | ||||
|     border: 2px solid var(--border); | ||||
|     border-radius: var(--s); | ||||
|     padding: 4px 8px; | ||||
|     font-weight: bold; | ||||
|     display: inline-block; | ||||
| } | ||||
|  | ||||
| .button-container { | ||||
|     border: none !important; | ||||
|     mask-repeat: no-repeat !important; | ||||
|   | ||||
| @@ -18,7 +18,7 @@ | ||||
|                      title="{% trans %} %actor_nickname%'s avatar{% endtrans %}" | ||||
|                      width="{{ actor_avatar_dimensions['width'] }}" | ||||
|                      height="{{ actor_avatar_dimensions['height'] }}"> | ||||
|                 <div> | ||||
|                 <section> | ||||
|                     <a class="profile-info-url" href="{{ actor_uri }}"> | ||||
|                         <strong class="profile-info-url-nickname" | ||||
|                                 title="{% trans %} %actor_nickname%'s nickname {% endtrans %}"> | ||||
| @@ -39,15 +39,17 @@ | ||||
|                             </li> | ||||
|                         {% endfor %} | ||||
|                     </ul> | ||||
|                 </div> | ||||
|                 </section> | ||||
|             </div> | ||||
|             <div class="profile-stats"> | ||||
|                 <span class="profile-stats-subscriptions" title="{% trans %} %actor_nickname%'s subscribed count{% endtrans %}"> | ||||
|                 <span class="profile-stats-subscriptions" | ||||
|                       title="{% trans %} %actor_nickname%'s subscribed count{% endtrans %}"> | ||||
|                     <strong> | ||||
|                         <a href="{{ actor.getSubscriptionsUrl() }}">{{ 'Subscribed' | trans }}</a> | ||||
|                     </strong>{{ actor.getSubscribedCount() }} | ||||
|                 </span> | ||||
|                 <span class="profile-stats-subscribers" title="{% trans %} %actor_nickname%'s subscribers count{% endtrans %}"> | ||||
|                 <span class="profile-stats-subscribers" | ||||
|                       title="{% trans %} %actor_nickname%'s subscribers count{% endtrans %}"> | ||||
|                     <strong> | ||||
|                         <a href="{{ actor.getSubscribersUrl() }}">{{ 'Subscribers' | trans }}</a> | ||||
|                     </strong>{{ actor.getSubscribersCount() }} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user