Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
96.72% covered (success)
96.72%
59 / 61
80.00% covered (success)
80.00%
4 / 5
CRAP
0.00% covered (danger)
0.00%
0 / 1
GroupController
96.72% covered (success)
96.72%
59 / 61
80.00% covered (success)
80.00%
4 / 5
11
0.00% covered (danger)
0.00%
0 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 index
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
1
 add
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
1
 create
93.55% covered (success)
93.55%
29 / 31
0.00% covered (danger)
0.00%
0 / 1
7.01
 getBaseTemplateVars
100.00% covered (success)
100.00%
12 / 12
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3/**
4 * The Group Administration Controller
5 *
6 * This Source Code Form is subject to the terms of the Mozilla Public License,
7 * v. 2.0. If a copy of the MPL was not distributed with this file, You can
8 * obtain one at https://mozilla.org/MPL/2.0/.
9 *
10 * @package   phpMyFAQ
11 * @author    Thorsten Rinne <thorsten@phpmyfaq.de>
12 * @copyright 2024-2026 phpMyFAQ Team
13 * @license   https://www.mozilla.org/MPL/2.0/ Mozilla Public License Version 2.0
14 * @link      https://www.phpmyfaq.de
15 * @since     2024-11-24
16 */
17
18declare(strict_types=1);
19
20namespace phpMyFAQ\Controller\Administration;
21
22use phpMyFAQ\Core\Exception;
23use phpMyFAQ\Enums\AdminLogType;
24use phpMyFAQ\Enums\PermissionType;
25use phpMyFAQ\Filter;
26use phpMyFAQ\Permission\MediumPermission;
27use phpMyFAQ\Session\Token;
28use phpMyFAQ\Translation;
29use phpMyFAQ\Twig\Extensions\PermissionTranslationTwigExtension;
30use phpMyFAQ\User;
31use Symfony\Component\HttpFoundation\Request;
32use Symfony\Component\HttpFoundation\Response;
33use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
34use Symfony\Component\Routing\Attribute\Route;
35use Twig\Error\LoaderError;
36use Twig\Extension\AttributeExtension;
37
38final class GroupController extends AbstractAdministrationController
39{
40    public function __construct(
41        private readonly User $user,
42    ) {
43        parent::__construct();
44    }
45
46    /**
47     * @throws Exception
48     * @throws LoaderError
49     * @throws \Exception
50     */
51    #[Route(path: '/group', name: 'admin.group', methods: ['GET'])]
52    public function index(Request $request): Response
53    {
54        $this->userHasPermission(PermissionType::GROUP_ADD);
55        $this->userHasPermission(PermissionType::GROUP_EDIT);
56        $this->userHasPermission(PermissionType::GROUP_DELETE);
57
58        $this->addExtension(new AttributeExtension(PermissionTranslationTwigExtension::class));
59        return $this->render('@admin/user/group.twig', [
60            ...$this->getHeader($request),
61            ...$this->getFooter(),
62            ...$this->getBaseTemplateVars(),
63        ]);
64    }
65
66    /**
67     * @throws LoaderError
68     * @throws Exception
69     * @throws \Exception
70     */
71    #[Route(path: '/group/add', name: 'admin.group.add', methods: ['GET'])]
72    public function add(Request $request): Response
73    {
74        $this->userHasPermission(PermissionType::GROUP_ADD);
75
76        $this->addExtension(new AttributeExtension(PermissionTranslationTwigExtension::class));
77        return $this->render('@admin/user/group.add.twig', [
78            ...$this->getHeader($request),
79            ...$this->getFooter(),
80            ...$this->getBaseTemplateVars(),
81            'csrfAddGroup' => Token::getInstance($this->session)->getTokenString('add-group'),
82        ]);
83    }
84
85    /**
86     * @throws Exception
87     * @throws LoaderError
88     * @throws \Exception
89     */
90    #[Route(path: '/group/create', name: 'admin.group.create', methods: ['POST'])]
91    public function create(Request $request): Response
92    {
93        $this->userHasPermission(PermissionType::GROUP_ADD);
94
95        $csrfToken = $request->attributes->get('csrf');
96        if (!Token::getInstance($this->session)->verifyToken('add-group', is_string($csrfToken) ? $csrfToken : null)) {
97            throw new UnauthorizedHttpException('Invalid CSRF token');
98        }
99
100        $groupName = Filter::filterVar($request->request->get('group_name'), FILTER_SANITIZE_SPECIAL_CHARS);
101        $groupDescription = Filter::filterVar(
102            $request->request->get('group_description'),
103            FILTER_SANITIZE_SPECIAL_CHARS,
104        );
105        $groupAutoJoin = Filter::filterVar($request->request->get('group_auto_join'), FILTER_SANITIZE_SPECIAL_CHARS);
106
107        // check group name
108        if ($groupName === '') {
109            $message = Translation::get(key: 'ad_group_error_noName');
110        }
111
112        $groupData = [
113            'name' => (string) $groupName,
114            'description' => (string) $groupDescription,
115            'auto_join' => (string) $groupAutoJoin,
116        ];
117
118        $groupId = $this->user->perm instanceof MediumPermission ? $this->user->perm->addGroup($groupData) : 0;
119        $message = '';
120        if ($groupId === 0) {
121            $message = sprintf('<div class="alert alert-danger">%s</div>', Translation::getString('ad_adus_dberr'));
122        }
123
124        if ($groupId !== 0) {
125            $this->adminLog->log($this->currentUser, AdminLogType::GROUP_ADD->value . ':' . $groupId);
126            $message = sprintf('<div class="alert alert-success">%s</div>', Translation::getString('ad_group_suc'));
127        }
128
129        $this->addExtension(new AttributeExtension(PermissionTranslationTwigExtension::class));
130        return $this->render('@admin/user/group.twig', [
131            ...$this->getHeader($request),
132            ...$this->getFooter(),
133            ...$this->getBaseTemplateVars(),
134            'message' => $message,
135        ]);
136    }
137
138    /**
139     * @throws \Exception
140     * @return array<string, mixed>
141     */
142    private function getBaseTemplateVars(): array
143    {
144        return [
145            'rightData' => $this->user->perm->getAllRightsData(),
146            'csrfTokenDeleteGroup' => Token::getInstance($this->session)->getTokenString('delete-group'),
147            'csrfTokenUpdateGroup' => Token::getInstance($this->session)->getTokenString('update-group'),
148            'csrfTokenUpdateGroupMembers' => Token::getInstance($this->session)->getTokenString('update-group-members'),
149            'csrfTokenUpdateGroupPermissions' => Token::getInstance($this->session)->getTokenString(
150                'update-group-permissions',
151            ),
152            'csrfTokenCategoryRestrictions' => Token::getInstance($this->session)->getTokenString(
153                'save-category-restrictions',
154            ),
155        ];
156    }
157}