Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
87 / 87
100.00% covered (success)
100.00%
9 / 9
CRAP
100.00% covered (success)
100.00%
1 / 1
RegistrationController
100.00% covered (success)
100.00%
87 / 87
100.00% covered (success)
100.00%
9 / 9
29
100.00% covered (success)
100.00%
1 / 1
 __construct
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 view
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
 save
100.00% covered (success)
100.00%
15 / 15
100.00% covered (success)
100.00%
1 / 1
6
 notifySignUpDisabledAndRedirectToHome
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 renderSignUpPage
100.00% covered (success)
100.00%
30 / 30
100.00% covered (success)
100.00%
1 / 1
2
 determinePreselectedAngelTypes
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
4
 loadAngelTypesFromSessionOAuthGroups
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
 loadAngelTypesFromSessionFormData
100.00% covered (success)
100.00%
8 / 8
100.00% covered (success)
100.00%
1 / 1
3
 determineRegistrationDisabled
100.00% covered (success)
100.00%
13 / 13
100.00% covered (success)
100.00%
1 / 1
7
1<?php
2
3declare(strict_types=1);
4
5namespace Engelsystem\Controllers;
6
7use Engelsystem\Config\Config;
8use Engelsystem\Config\GoodieType;
9use Engelsystem\Events\Listener\OAuth2;
10use Engelsystem\Factories\User;
11use Engelsystem\Helpers\Authenticator;
12use Engelsystem\Http\Redirector;
13use Engelsystem\Http\Request;
14use Engelsystem\Http\Response;
15use Engelsystem\Models\AngelType;
16use Symfony\Component\HttpFoundation\Session\SessionInterface;
17
18class RegistrationController extends BaseController
19{
20    use HasUserNotifications;
21
22    public function __construct(
23        private Config $config,
24        private Response $response,
25        private Redirector $redirect,
26        private SessionInterface $session,
27        private Authenticator $auth,
28        private OAuth2 $oAuth,
29        private User $userFactory
30    ) {
31    }
32
33    public function view(): Response
34    {
35        if ($this->determineRegistrationDisabled()) {
36            return $this->notifySignUpDisabledAndRedirectToHome();
37        }
38
39        return $this->renderSignUpPage();
40    }
41
42    public function save(Request $request): Response
43    {
44        if ($this->determineRegistrationDisabled()) {
45            return $this->notifySignUpDisabledAndRedirectToHome();
46        }
47
48        $rawData = $request->getParsedBody();
49        $user = $this->userFactory->createFromData($rawData);
50
51        if (!$this->auth->user()) {
52            $this->addNotification('registration.successful');
53        } else {
54            $this->addNotification('registration.successful.supporter');
55        }
56
57        if ($this->config->get('welcome_msg')) {
58            // Set a session marker to display the welcome message on the next page
59            $this->session->set('show_welcome', true);
60        }
61
62        if ($user->oauth?->count() > 0) {
63            // User has OAuth configured. Log in directly.
64            $provider = $user->oauth->first();
65            return $this->redirect->to('/oauth/' . $provider->provider);
66        }
67
68        if ($this->auth->user()) {
69            // User is already logged in - that means a supporter has registered an angel. Return to register page.
70            return $this->redirect->to('/register');
71        }
72
73        return $this->redirect->to('/');
74    }
75
76    private function notifySignUpDisabledAndRedirectToHome(): Response
77    {
78        $this->addNotification('registration.disabled', NotificationType::INFORMATION);
79        return $this->redirect->to('/');
80    }
81
82    private function renderSignUpPage(): Response
83    {
84        $goodieType = GoodieType::from($this->config->get('goodie_type'));
85        $preselectedAngelTypes = $this->determinePreselectedAngelTypes();
86        $requiredFields = $this->config->get('required_user_fields');
87
88        // form-data-register-submit is a marker, that the form was submitted.
89        // It will be used for instance to use the default angel types or the user selected ones.
90        // Clear it before render to reset the marker state.
91        $this->session->remove('form-data-register-submit');
92
93        return $this->response->withView(
94            'pages/registration',
95            [
96                'minPasswordLength' => $this->config->get('password_min_length'),
97                'tShirtSizes' => $this->config->get('tshirt_sizes'),
98                'tShirtLink' => $this->config->get('tshirt_link'),
99                'angelTypes' => AngelType::whereHideRegister(false)->get(),
100                'preselectedAngelTypes' => $preselectedAngelTypes,
101                'buildUpStartDate' => $this->userFactory->determineBuildUpStartDate()->format('Y-m-d'),
102                'tearDownEndDate' => $this->config->get('teardown_end')?->format('Y-m-d'),
103                'isPasswordEnabled' => $this->userFactory->determineIsPasswordEnabled(),
104                'isDECTEnabled' => $this->config->get('enable_dect'),
105                'isShowMobileEnabled' => $this->config->get('enable_mobile_show'),
106                'isGoodieEnabled' => $goodieType !== GoodieType::None && config('enable_email_goodie'),
107                'isGoodieTShirt' => $goodieType === GoodieType::Tshirt,
108                'isPronounEnabled' => $this->config->get('enable_pronoun'),
109                'isFullNameEnabled' => $this->config->get('enable_full_name'),
110                'isPlannedArrivalDateEnabled' => $this->config->get('enable_planned_arrival'),
111                'isPronounRequired' => $requiredFields['pronoun'],
112                'isFirstnameRequired' => $requiredFields['firstname'],
113                'isLastnameRequired' => $requiredFields['lastname'],
114                'isTShirtSizeRequired' => $requiredFields['tshirt_size'],
115                'isMobileRequired' => $requiredFields['mobile'],
116                'isDectRequired' => $requiredFields['dect'],
117            ],
118        );
119    }
120
121    /**
122     * @return Array<string, 1> Checkbox field name/id â†’  1
123     */
124    private function determinePreselectedAngelTypes(): array
125    {
126        if ($this->session->has('form-data-register-submit')) {
127            // form-data-register-submit means a user just submitted the page.
128            // Preselect the angel types from the persisted session form data.
129            return $this->loadAngelTypesFromSessionFormData();
130        }
131
132        $preselectedAngelTypes = [];
133
134        if ($this->session->has('oauth2_connect_provider')) {
135            $preselectedAngelTypes = $this->loadAngelTypesFromSessionOAuthGroups();
136        }
137
138        foreach (AngelType::whereRestricted(false)->whereHideRegister(false)->get() as $angelType) {
139            // preselect every angel type without restriction
140            $preselectedAngelTypes['angel_types_' . $angelType->id] = 1;
141        }
142
143        return $preselectedAngelTypes;
144    }
145
146    /**
147     * @return Array<string, 1>
148     */
149    private function loadAngelTypesFromSessionOAuthGroups(): array
150    {
151        $oAuthAngelTypes = [];
152        $ssoTeams = $this->oAuth->getSsoTeams($this->session->get('oauth2_connect_provider'));
153        $oAuth2Groups = $this->session->get('oauth2_groups');
154
155        foreach ($ssoTeams as $name => $team) {
156            if (in_array($name, $oAuth2Groups)) {
157                // preselect angel type from oauth
158                $oAuthAngelTypes['angel_types_' . $team['id']] = 1;
159            }
160        }
161
162        return $oAuthAngelTypes;
163    }
164
165    /**
166     * @return Array<string, 1>
167     */
168    private function loadAngelTypesFromSessionFormData(): array
169    {
170        $angelTypes = AngelType::whereHideRegister(false)->get();
171        $selectedAngelTypes = [];
172
173        foreach ($angelTypes as $angelType) {
174            $sessionKey = 'form-data-angel_types_' . $angelType->id;
175
176            if ($this->session->has($sessionKey)) {
177                $selectedAngelTypes['angel_types_' . $angelType->id] = 1;
178                // remove from session so that it doesn't stay there forever
179                $this->session->remove($sessionKey);
180            }
181        }
182
183        return $selectedAngelTypes;
184    }
185
186    private function determineRegistrationDisabled(): bool
187    {
188        $authUser = $this->auth->user();
189        $isOAuth = $this->session->get('oauth2_connect_provider');
190        $isPasswordEnabled = $this->userFactory->determineIsPasswordEnabled();
191
192        return !auth()->can('register') // No registration permission
193            // Not authenticated and
194            // Registration disabled
195            || (
196                !$authUser
197                && !$this->config->get('registration_enabled')
198                && !$this->session->get('oauth2_allow_registration')
199            )
200            // Password disabled and not oauth
201            || (!$authUser && !$isPasswordEnabled && !$isOAuth);
202    }
203}