Passed
Push — master ( 9bed10...cf9f86 )
by Derek Stephen
02:25
created

BoneUserController::forgotPasswordAction()   A

Complexity

Conditions 4
Paths 13

Size

Total Lines 39
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 25
CRAP Score 4

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 25
dl 0
loc 39
ccs 25
cts 25
cp 1
rs 9.52
c 1
b 0
f 0
cc 4
nc 13
nop 1
crap 4
1
<?php declare(strict_types=1);
2
3
namespace Bone\User\Controller;
4
5
use Bone\Http\Response\HtmlResponse;
6
use Bone\Http\Response\LayoutResponse;
7
use Bone\I18n\Form;
8
use Bone\Controller\Controller;
9
use Bone\Paseto\PasetoService;
10
use Bone\Server\SiteConfigAwareInterface;
11
use Bone\Server\Traits\HasSiteConfigTrait;
12
use Bone\View\ViewEngine;
13
use Bone\Server\SessionAwareInterface;
14
use Bone\Server\Traits\HasSessionTrait;
15
use Bone\Mail\EmailMessage;
16
use Bone\Mail\Service\MailService;
17
use Bone\User\Form\LoginForm;
18
use Bone\User\Form\PersonForm;
19
use Bone\User\Form\RegistrationForm;
20
use Bone\User\Form\ResetPasswordForm;
21
use DateTime;
22
use Del\Entity\User;
23
use Del\Exception\EmailLinkException;
24
use Del\Exception\UserException;
25
use Del\Factory\CountryFactory;
26
use Del\Form\Field\Text\EmailAddress;
27
use Del\Icon;
28
use Del\Service\UserService;
29
use Del\SessionManager;
30
use Del\Value\User\State;
31
use Exception;
32
use Psr\Http\Message\ResponseInterface;
33
use Psr\Http\Message\ServerRequestInterface;
34
use Laminas\Diactoros\Response\RedirectResponse;
35
use Laminas\Diactoros\Uri;
36
37
class BoneUserController extends Controller implements SessionAwareInterface, SiteConfigAwareInterface
38
{
39
    use HasSessionTrait;
40
    use HasSiteConfigTrait;
41
42
    /** @var UserService $userService */
43
    private $userService;
44
45
    /** @var MailService $mailService */
46
    private $mailService;
47
48
    /** @var string $loginRedirectRoute */
49
    private $loginRedirectRoute;
50
51
    /** @var string $logo */
52
    private $logo;
53
54
    /** @var string $adminLayout */
55
    private $adminLayout;
56
57
    /** @var bool $registrationEnabled */
58
    private $registrationEnabled;
59
60
    /** @var bool $profileRequired */
61
    private $profileRequired;
62
63
    /** @var bool $rememberMeCookie */
64
    private $rememberMeCookie;
65
66
    /** @var PasetoService $pasetoService */
67
    private $pasetoService = null;
68
69
    /**
70
     * BoneUserController constructor.
71
     * @param UserService $userService
72
     * @param MailService $mailService
73
     */
74 27
    public function __construct(UserService $userService, MailService $mailService, string $loginRedirectRoute = '/user/home',
75
                                string $adminLayout, PasetoService $pasetoService, bool $registrationEnabled = true, $profileRequired = false,
76
                                bool $rememberMeCookie = true)
77
    {
78 27
        $this->userService = $userService;
79 27
        $this->mailService = $mailService;
80 27
        $this->loginRedirectRoute = $loginRedirectRoute;
81 27
        $this->adminLayout = $adminLayout;
82 27
        $this->registrationEnabled = $registrationEnabled;
83 27
        $this->profileRequired = $profileRequired;
84 27
        $this->rememberMeCookie = $rememberMeCookie;
85 27
        $this->profileRequired = $profileRequired;
86 27
        $this->pasetoService = $pasetoService;
87 27
    }
88
89
    /**
90
     * @return string
91
     */
92 19
    public function getLogo(): string
93
    {
94 19
        if (!$this->logo) {
95 19
            $this->logo = $this->getSiteConfig()->getLogo();
96
        }
97
98 19
        return $this->logo;
99
    }
100
101
    /**
102
     * @param ServerRequestInterface $request
103
     * @return ResponseInterface $response
104
     * @throws \Exception
105
     */
106 1
    public function indexAction(ServerRequestInterface $request): ResponseInterface
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

106
    public function indexAction(/** @scrutinizer ignore-unused */ ServerRequestInterface $request): ResponseInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
107
    {
108 1
        if ($this->getSession()->get('user')) {
109 1
            return new RedirectResponse('/user/home');
110
        }
111
112 1
        $body = $this->getView()->render('boneuser::index', ['logo' => $this->getLogo()]);
113
114 1
        return new HtmlResponse($body);
115
    }
116
117
    /**
118
     * @param ServerRequestInterface $request
119
     * @return ResponseInterface
120
     * @throws UserException
121
     */
122 4
    public function registerAction(ServerRequestInterface $request): ResponseInterface
123
    {
124 4
        $form = new RegistrationForm('register', $this->getTranslator());
125 4
        $message = null;
126
127 4
        if ($request->getMethod() == 'POST') {
128
129 3
            $formData = $request->getParsedBody();
130 3
            $form->populate($formData);
0 ignored issues
show
Bug introduced by
It seems like $formData can also be of type null and object; however, parameter $data of Del\Form\AbstractForm::populate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

130
            $form->populate(/** @scrutinizer ignore-type */ $formData);
Loading history...
131
132 3
            if ($form->isValid()) {
133 2
                $data = $form->getValues();
134
                try {
135 2
                    $user = $this->userService->registerUser($data);
136 1
                    $link = $this->userService->generateEmailLink($user);
137 1
                    $mail = $this->mailService;
138
139 1
                    $env = $mail->getSiteConfig()->getEnvironment();
140 1
                    $email = $user->getEmail();
141 1
                    $token = $link->getToken();
142
143 1
                    $mail = new EmailMessage();
144 1
                    $mail->setTo($user->getEmail());
145 1
                    $mail->setSubject($this->getTranslator()->translate('email.user.register.thankswith', 'user') . ' ' . $this->mailService->getSiteConfig()->getTitle());
146 1
                    $mail->setTemplate('email.user::user_registration/user_registration');
147 1
                    $mail->setViewData([
148 1
                        'siteUrl' => $env->getSiteURL(),
149 1
                        'logo' => $this->getSiteConfig()->getEmailLogo(),
150 1
                        'address' => $this->getSiteConfig()->getAddress(),
151 1
                        'activationLink' => '/user/activate/' . $email . '/' . $token,
152
                    ]);
153 1
                    $this->mailService->sendEmail($mail);
154 1
                    $body = $this->getView()->render('boneuser::thanks-for-registering', ['logo' => $this->getLogo()]);
155
156 1
                    return new HtmlResponse($body);
157
158 1
                } catch (UserException $e) {
159 1
                    $message = [$e->getMessage(), 'danger'];
160
                }
161
            } else {
162 1
                $message = [Icon::WARNING . ' There was a problem with your form.', 'danger'];
163
            }
164
        }
165
166 3
        $body = $this->getView()->render('boneuser::register', ['form' => $form, 'message' => $message, 'logo' => $this->getLogo()]);
167
168 3
        return new HtmlResponse($body);
169
    }
170
171
    /**
172
     * @param ServerRequestInterface $request
173
     * @return ResponseInterface
174
     * @throws \Doctrine\ORM\ORMException
175
     * @throws \Doctrine\ORM\OptimisticLockException
176
     */
177 3
    public function activateAction(ServerRequestInterface $request): ResponseInterface
178
    {
179 3
        $email = $request->getAttribute('email');
180 3
        $token = $request->getAttribute('token');
181 3
        $translator = $this->getTranslator();
182 3
        $userService = $this->userService;
183 3
        $loginRedirect = $this->loginRedirectRoute;
184 3
        $message = null;
185
186
        try {
187
188 3
            $link = $userService->findEmailLink($email, $token);
189 1
            $user = $link->getUser();
190 1
            $user->setState(new State(State::STATE_ACTIVATED));
191 1
            $user->setLastLogin(new DateTime());
192 1
            $userService->saveUser($user);
193 1
            $userService->deleteEmailLink($link);
194 1
            $this->getSession()->set('user', $user->getId());
195
196 2
        } catch (EmailLinkException $e) {
197 2
            switch ($e->getMessage()) {
198 2
                case EmailLinkException::LINK_EXPIRED:
199 1
                    $message = [$translator->translate('login.activation.expired', 'user')
200 1
                        . ' <a href="/user/resend-activation-mail/' . $email . '">'
201 1
                        . $translator->translate('login.activation.expired2', 'user') . '</a>', 'danger'];
202 1
                    break;
203
                default:
204 1
                    $message = [$e->getMessage(), 'danger'];
205 1
                    break;
206
            }
207
        }
208
209 3
        $body = $this->getView()->render('boneuser::activate-user-account', [
210 3
            'loginRedirect' => $loginRedirect,
211 3
            'message' => $message,
212 3
            'logo' => $this->getLogo(),
213
        ]);
214
215 3
        return new HtmlResponse($body);
216
    }
217
218 7
    private function initForm(LoginForm $form)
219
    {
220 7
        if ($this->rememberMeCookie === false) {
221 7
            $form->getFields()->removeByName('remember');
222
        }
223 7
    }
224
225
226
    /**
227
     * @param ServerRequestInterface $request
228
     * @return ResponseInterface
229
     */
230 1
    public function loginAction(ServerRequestInterface $request): ResponseInterface
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

230
    public function loginAction(/** @scrutinizer ignore-unused */ ServerRequestInterface $request): ResponseInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
231
    {
232 1
        $form = new LoginForm('userlogin', $this->getTranslator());
233 1
        $this->initForm($form);
234 1
        $body = $this->getView()->render('boneuser::login', ['form' => $form, 'logo' => $this->getLogo()]);
235
236 1
        return new HtmlResponse($body);
237
    }
238
239
240
    /**
241
     * @param ServerRequestInterface $request
242
     * @return ResponseInterface
243
     */
244 6
    public function loginFormAction(ServerRequestInterface $request): ResponseInterface
245
    {
246 6
        $translator = $this->getTranslator();
247 6
        $form = new LoginForm('userlogin', $translator);
248 6
        $this->initForm($form);
249 6
        $post = $request->getParsedBody() ?: [];
250 6
        $form->populate($post);
1 ignored issue
show
Bug introduced by
It seems like $post can also be of type object; however, parameter $data of Del\Form\AbstractForm::populate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

250
        $form->populate(/** @scrutinizer ignore-type */ $post);
Loading history...
251 6
        $params = ['form' => $form];
252
253
        try {
254
255 6
            if ($form->isValid()) {
256 5
                $data = $form->getValues();
257 5
                $email = $data['email'];
258 5
                $pass = $data['password'];
259 5
                $userId = $this->userService->authenticate($email, $pass);
260 5
                $locale = $translator->getLocale();
261 5
                $session = $this->getSession();
262 5
                $session->set('user', $userId);
263 5
                $session->set('locale', $locale);
264 5
                $this->rememberMeCookie && isset($data['remember']) ? $this->setCookie((int)$data['remember'], $userId) : null;
265
266 5
                if ($route = $session->get('loginRedirectRoute')) {
267 5
                    $this->loginRedirectRoute = $route;
268 5
                    $session->unset('loginRedirectRoute');
269
                }
270
271 5
                $user = $this->userService->findUserById($userId);
272 1
                $user->setLastLogin(new DateTime());
273 1
                $this->userService->saveUser($user);
0 ignored issues
show
Bug introduced by
It seems like $user can also be of type null; however, parameter $user of Del\Service\UserService::saveUser() does only seem to accept Del\Entity\UserInterface, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

273
                $this->userService->saveUser(/** @scrutinizer ignore-type */ $user);
Loading history...
274
275 1
                if ($this->profileRequired && !$this->userService->hasProfile($user)) {
0 ignored issues
show
Bug introduced by
It seems like $user can also be of type null; however, parameter $user of Del\Service\UserService::hasProfile() does only seem to accept Del\Entity\User, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

275
                if ($this->profileRequired && !$this->userService->hasProfile(/** @scrutinizer ignore-type */ $user)) {
Loading history...
276 1
                    $this->loginRedirectRoute = '/user/edit-profile';
277
                }
278
279 2
                return new RedirectResponse('/' . $locale . $this->loginRedirectRoute);
280
            }
281 4
        } catch (UserException $e) {
282 4
            switch ($e->getMessage()) {
283 4
                case UserException::USER_NOT_FOUND:
284 4
                case UserException::WRONG_PASSWORD:
285 1
                    $message = [Icon::WARNING . ' ' . $translator->translate('login.error.password', 'user') . '<a href="/user/lost-password/' . $email . '">' . $translator->translate('login.error.password2', 'user') . '</a>', 'danger'];
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $email does not seem to be defined for all execution paths leading up to this point.
Loading history...
286 1
                    break;
287 3
                case UserException::USER_UNACTIVATED:
288 1
                    $message = [Icon::WARNING . ' ' . $translator->translate('login.unactivated', 'user') . '<a href="/user/resend-activation-mail/' . $email . '">' . $translator->translate('login.unactivated2', 'user') . '</a>', 'danger'];
289 1
                    break;
290 2
                case UserException::USER_DISABLED:
291 2
                case UserException::USER_BANNED:
292 1
                    $message = [Icon::REMOVE . ' ' . $translator->translate('login.activation.banned', 'user'), 'danger'];
293 1
                    break;
294
                default:
295 1
                    $message = $e->getMessage();
296 1
                    break;
297
            }
298
299 4
            $params['message'] = $message;
300
        }
301
302 5
        $params['logo'] = $this->getLogo();
303 5
        $body = $this->getView()->render('boneuser::login', $params);
304
305 5
        return new HtmlResponse($body);
306
307
    }
308
309
    /**
310
     * @param int $length
311
     * @param int $userId
312
     * @throws \ParagonIE\Paseto\Exception\InvalidKeyException
313
     * @throws \ParagonIE\Paseto\Exception\InvalidPurposeException
314
     * @throws \ParagonIE\Paseto\Exception\PasetoException
315
     */
316
    private function setCookie(int $length, int $userId): void
317
    {
318
        switch ($length) {
319
            case 1:
320
                $time = 60 * 60 * 24 * 7;
321
                break;
322
            case 1:
323
                $time = 60 * 60 * 24 * 30;
324
                break;
325
            case 1:
326
                $time = 60 * 60 * 24 * 365;
327
                break;
328
            default:
329
                $time = 0;
330
                break;
331
        }
332
        $time = time() + $time;
0 ignored issues
show
Unused Code introduced by
The assignment to $time is dead and can be removed.
Loading history...
333
        $token = $this->pasetoService->encryptToken([
334
            'user' => $userId,
335
        ]);
336
        \setcookie('resu', $token, \time() * 2, '/');
337
    }
338
339
    /**
340
     * @param ServerRequestInterface $request
341
     * @return ResponseInterface
342
     */
343 2
    public function homePageAction(ServerRequestInterface $request): ResponseInterface
344
    {
345 2
        if ($this->loginRedirectRoute !== '/user/home') {
346 1
            return new RedirectResponse($this->loginRedirectRoute);
347
        }
348
349 1
        $user = $request->getAttribute('user');
350 1
        $body = $this->getView()->render('boneuser::home', [
351 1
            'message' => [$this->getTranslator()->translate('home.loggedin', 'user'), 'success'],
352 1
            'user' => $user,
353 1
            'logo' => $this->getSiteConfig()->getLogo(),
354
        ]);
355
356 1
        return new LayoutResponse($body, $this->adminLayout);
357
    }
358
359
    /**
360
     * @param ServerRequestInterface $request
361
     * @return ResponseInterface
362
     */
363 1
    public function logoutAction(ServerRequestInterface $request): ResponseInterface
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

363
    public function logoutAction(/** @scrutinizer ignore-unused */ ServerRequestInterface $request): ResponseInterface

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
364
    {
365 1
        SessionManager::destroySession();
366 1
        \setcookie('resu', '', 1, '/');
367
368 1
        return new RedirectResponse(new Uri('/'));
369
    }
370
371
    /**
372
     * @param ServerRequestInterface $request
373
     * @return ResponseInterface
374
     */
375 4
    public function resendActivationEmailAction(ServerRequestInterface $request): ResponseInterface
376
    {
377 4
        $success = false;
0 ignored issues
show
Unused Code introduced by
The assignment to $success is dead and can be removed.
Loading history...
378 4
        $email = $request->getAttribute('email');
379 4
        $user = $this->userService->findUserByEmail($email);
380 4
        $message = [];
381 4
        $translator = $this->getTranslator();
382
383 4
        if (!$user) {
384 1
            throw new Exception(UserException::USER_NOT_FOUND, 404);
385
        }
386
387 3
        if ($user->getState()->getValue() == State::STATE_ACTIVATED) {
388 1
            $message = [UserException::USER_ACTIVATED, 'danger'];
0 ignored issues
show
Unused Code introduced by
The assignment to $message is dead and can be removed.
Loading history...
389
        } else {
390
            try {
391 2
                $link = $this->userService->generateEmailLink($user);
392 1
                $mail = $this->mailService;
393
394 1
                $env = $mail->getSiteConfig()->getEnvironment();
395 1
                $email = $user->getEmail();
396 1
                $token = $link->getToken();
397
398 1
                $mail = new EmailMessage();
399 1
                $mail->setTo($user->getEmail());
400 1
                $mail->setSubject($translator->translate('email.user.register.thankswith', 'user') . ' ' . $this->mailService->getSiteConfig()->getTitle());
401 1
                $mail->setTemplate('email.user::user_registration/user_registration');
402 1
                $mail->setViewData([
403 1
                    'siteUrl' => $env->getSiteURL(),
404 1
                    'logo' => $this->getSiteConfig()->getEmailLogo(),
405 1
                    'address' => $this->getSiteConfig()->getAddress(),
406 1
                    'activationLink' => '/user/activate/' . $email . '/' . $token,
407
                ]);
408 1
                $this->mailService->sendEmail($mail);
409
410 1
            } catch (Exception $e) {
411 1
                $message = [$translator->translate('login.resendactivation.error', 'user')
412 1
                    . $this->getSiteConfig()->getContactEmail() . '', 'danger'];
413
            }
414
        }
415
416 3
        $body = $this->getView()->render('boneuser::resend-activation', [
417 3
            'message' => null,
418 3
            'logo' => $this->getLogo(),
419
        ]);
420
421 3
        return new HtmlResponse($body);
422
    }
423
424
425
    /**
426
     * @param ServerRequestInterface $request
427
     * @return ResponseInterface
428
     */
429 4
    public function forgotPasswordAction(ServerRequestInterface $request): ResponseInterface
430
    {
431 4
        $email = $request->getAttribute('email');
432 4
        $user = $this->userService->findUserByEmail($email);
433
434 4
        if (!$user) {
435 1
            throw new Exception(UserException::USER_NOT_FOUND, 404);
436
        }
437
438 3
        if ($user->getState()->getValue() == State::STATE_UNACTIVATED) {
439 1
            return new RedirectResponse('/user/resend-activation-mail/' . $email);
440
        }
441
442
        try {
443
444 2
            $link = $this->userService->generateEmailLink($user);
445 1
            $email = $user->getEmail();
446 1
            $token = $link->getToken();
447 1
            $env = $this->getSiteConfig()->getEnvironment();
448 1
            $mail = new EmailMessage();
449 1
            $mail->setTo($email);
450 1
            $mail->setSubject($this->getTranslator()->translate('email.forgotpass.subject', 'user') . $this->mailService->getSiteConfig()->getTitle() . '.');
451 1
            $mail->setTemplate('email.user::user_registration/reset_password');
452 1
            $mail->setViewData([
453 1
                'siteUrl' => $env->getSiteURL(),
454 1
                'logo' => $this->getSiteConfig()->getEmailLogo(),
455 1
                'address' => $this->getSiteConfig()->getAddress(),
456 1
                'resetLink' => '/user/reset-password/' . $email . '/' . $token,
457
            ]);
458 1
            $this->mailService->sendEmail($mail);
459
460
461 1
        } catch (Exception $e) {
462 1
            $this->view->message = [$e->getMessage(), 'danger'];
0 ignored issues
show
Bug introduced by
Accessing message on the interface Bone\View\ViewEngineInterface suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
463
        }
464
465 2
        $body = $this->getView()->render('boneuser::forgot-password', ['logo' => $this->getLogo()]);
466
467 2
        return new HtmlResponse($body);
468
    }
469
470
471
    /**
472
     * @param ServerRequestInterface $request
473
     * @return ResponseInterface
474
     */
475
    public function resetPasswordAction(ServerRequestInterface $request): ResponseInterface
476
    {
477
        $email = $request->getAttribute('email');
478
        $token = $request->getAttribute('token');
479
        $form = new ResetPasswordForm('resetpass');
480
        $translator = $this->getTranslator();
481
        $params = [];
482
        $success = false;
483
        $user = $this->userService->findUserByEmail($email);
484
485
        if (!$user) {
486
            throw new Exception(UserException::USER_NOT_FOUND, 404);
487
        }
488
489
        try {
490
            $link = $this->userService->findEmailLink($email, $token);
491
492
            if ($request->getMethod() === 'POST') {
493
494
                $data = $request->getParsedBody();
495
                $form->populate($data);
1 ignored issue
show
Bug introduced by
It seems like $data can also be of type null and object; however, parameter $data of Del\Form\AbstractForm::populate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

495
                $form->populate(/** @scrutinizer ignore-type */ $data);
Loading history...
496
497
                if ($form->isValid()) {
498
                    $data = $form->getValues();
499
500
                    if ($data['password'] === $data['confirm']) {
501
                        $this->userService->changePassword($user, $data['password']);
502
                        $this->userService->deleteEmailLink($link);
503
                        $message = [$translator->translate('email.resetpass.success', 'user'), 'success'];
504
                        $success = true;
505
                        SessionManager::set('user', $user->getId());
0 ignored issues
show
Bug Best Practice introduced by
The method Del\SessionManager::set() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

505
                        SessionManager::/** @scrutinizer ignore-call */ 
506
                                        set('user', $user->getId());
Loading history...
506
                    } else {
507
                        $message = [$translator->translate('email.resetpass.nomatch', 'user'), 'danger'];
508
                        $form = new ResetPasswordForm('resetpass');
509
                    }
510
                }
511
            }
512
        } catch (EmailLinkException $e) {
513
            $message = [$e->getMessage(), 'danger'];
514
        } catch (Exception $e) {
515
            throw $e;
516
        }
517
518
        if (isset($message)) {
519
            $params['message'] = $message;
520
        }
521
        
522
        $params['success'] = $success;
523
        $params['form'] = $form;
524
        $params['logo'] = $this->getLogo();
525
        $body = $this->getView()->render('boneuser::reset-pass', $params);
526
527
        return new HtmlResponse($body);
528
    }
529
530
    /**
531
     * @param ServerRequestInterface $request
532
     * @return ResponseInterface
533
     */
534
    public function changePasswordAction(ServerRequestInterface $request): ResponseInterface
535
    {
536
        $user = $request->getAttribute('user');
537
        $form = new ResetPasswordForm('resetpass');
538
        $translator = $this->getTranslator();
539
        $message = null;
540
        $success = false;
541
542
        if ($request->getMethod() === 'POST') {
543
544
            $data = $request->getParsedBody();
545
            $form->populate($data);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type null and object; however, parameter $data of Del\Form\AbstractForm::populate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

545
            $form->populate(/** @scrutinizer ignore-type */ $data);
Loading history...
546
547
            if ($form->isValid()) {
548
                $data = $form->getValues();
549
                if ($data['password'] === $data['confirm']) {
550
                    $this->userService->changePassword($user, $data['password']);
551
                    $message = [Icon::CHECK_CIRCLE . ' ' . $translator->translate('email.resetpass.success', 'user'), 'success'];
552
                    $success = true;
553
                } else {
554
                    $message = [Icon::WARNING . ' ' . $translator->translate('email.resetpass.nomatch', 'user'), 'danger'];
555
                    $form = new ResetPasswordForm('resetpass');
556
                }
557
            } else {
558
                $message = [Icon::WARNING . ' There was a problem with your form.', 'danger'];
559
            }
560
        }
561
562
        $params['success'] = $success;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$params was never initialized. Although not strictly required by PHP, it is generally a good practice to add $params = array(); before regardless.
Loading history...
563
        $params['form'] = $form;
564
        $params['logo'] = $this->getLogo();
565
566
        $body = $this->getView()->render('boneuser::change-pass', [
567
            'success' => $success,
568
            'form' => $form,
569
            'logo' => $this->getLogo(),
570
            'message' => $message
571
        ]);
572
573
        return new HtmlResponse($body);
574
    }
575
576
    /**
577
     * @param ServerRequestInterface $request
578
     * @return ResponseInterface
579
     */
580
    public function changeEmailAction(ServerRequestInterface $request): ResponseInterface
581
    {
582
        $user = $request->getAttribute('user');
583
        $form = new LoginForm('changeemail', $this->getTranslator());
584
        $form->getField('email')->setLabel('New email');
585
        $form->getField('submit')->setValue('Submit');
586
        $translator = $this->getTranslator();
587
        $params = [
588
            'form' => $form
589
        ];
590
591
        if ($request->getMethod() === 'POST') {
592
593
            $data = $request->getParsedBody();
594
            $form->populate($data);
0 ignored issues
show
Bug introduced by
It seems like $data can also be of type null and object; however, parameter $data of Del\Form\AbstractForm::populate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

594
            $form->populate(/** @scrutinizer ignore-type */ $data);
Loading history...
595
596
            if ($form->isValid($data)) {
0 ignored issues
show
Unused Code introduced by
The call to Del\Form\AbstractForm::isValid() has too many arguments starting with $data. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

596
            if ($form->/** @scrutinizer ignore-call */ isValid($data)) {

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
597
598
                $newEmail = $form->getField('email')->getValue();
599
                $password = $form->getField('password')->getValue();
600
601
                $existing = $this->userService->findUserByEmail($newEmail);
602
603
                if ($existing) {
604
                    $message = [$translator->translate('email.changeemail.registered', 'user') . $this->getSiteConfig()->getTitle() . '.', 'danger'];
605
                } else {
606
                    if ($this->userService->checkPassword($user, $password)) {
607
608
                        $link = $this->userService->generateEmailLink($user);
0 ignored issues
show
Unused Code introduced by
The assignment to $link is dead and can be removed.
Loading history...
609
610
                        try {
611
612
                            $link = $this->userService->generateEmailLink($user);
613
                            $email = $user->getEmail();
614
                            $token = $link->getToken();
615
                            $env = $this->getSiteConfig()->getEnvironment();
616
                            $mail = new EmailMessage();
617
                            $mail->setTo($email);
618
                            $mail->setSubject($translator->translate('email.changeemail.subject', 'user') . $this->mailService->getSiteConfig()->getTitle() . '.');
619
                            $mail->setTemplate('email.user::user_registration/change_email');
620
                            $mail->setViewData([
621
                                'siteUrl' => $env->getSiteURL(),
622
                                'logo' => $this->getSiteConfig()->getEmailLogo(),
623
                                'address' => $this->getSiteConfig()->getAddress(),
624
                                'resetLink' => '/user/reset-email/' . $email . '/' . $newEmail . '/' . $token,
625
                            ]);
626
                            $this->mailService->sendEmail($mail);
627
                            $message = [$translator->translate('email.changeemail.sent', 'user'), 'info'];
628
                            unset ($params['form']);
629
630
                        } catch (Exception $e) {
631
                            $message = [$translator->translate('email.changeemail.notsent', 'user') . $this->config->email->support . '.', 'danger'];
0 ignored issues
show
Bug Best Practice introduced by
The property config does not exist on Bone\User\Controller\BoneUserController. Did you maybe forget to declare it?
Loading history...
632
                        }
633
634
                    } else {
635
                        $message = [$translator->translate('email.changeemail.wrongpass', 'user'), 'danger'];
636
                    }
637
                }
638
            }
639
            $params['message'] = $message;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $message does not seem to be defined for all execution paths leading up to this point.
Loading history...
640
        }
641
        $params['logo'] = $this->getLogo();
642
643
        $body = $this->getView()->render('boneuser::change-email', $params);
644
645
        return new LayoutResponse($body, 'layouts::admin');
646
    }
647
648
    /**
649
     * @param ServerRequestInterface $request
650
     * @return ResponseInterface
651
     */
652
    public function editProfileAction(ServerRequestInterface $request): ResponseInterface
653
    {
654
        $user = $request->getAttribute('user');
655
        $person = $user->getPerson();
656
        $image = $person->getImage();
657
        $form = new PersonForm('profile', $this->getTranslator());
658
        $array = $this->userService->getPersonSvc()->toArray($person);
659
660
        $form->populate($array);
661
662
        if ($request->getMethod() === 'POST') {
663
            $post = $request->getParsedBody();
664
            $form->populate($post);
0 ignored issues
show
Bug introduced by
It seems like $post can also be of type null and object; however, parameter $data of Del\Form\AbstractForm::populate() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

664
            $form->populate(/** @scrutinizer ignore-type */ $post);
Loading history...
665
666
            if ($form->isValid()) {
667
                $data = $form->getValues();
668
                $data['image'] = $image;
669
                $dateFormat = $this->getSiteConfig()->getAttribute('i18n')['date_format'];
670
                $data['dob'] = DateTime::createFromFormat($dateFormat, $data['dob']);
671
                $data['country'] = CountryFactory::generate($data['country']);
672
                $this->userService->getPersonSvc()->populateFromArray($person, $data);
673
                $this->userService->saveUser($user);
674
            }
675
        }
676
677
        $body = $this->getView()->render('boneuser::edit-profile', [
678
            'person' => $person,
679
            'form' => $form->render(),
680
        ]);
681
682
        return new LayoutResponse($body, $this->adminLayout);
683
    }
684
685
    /**
686
     * @param ServerRequestInterface $requestApiController
687
     * @return ResponseInterface
688
     */
689
    public function resetEmailAction(ServerRequestInterface $request): ResponseInterface
690
    {
691
        $email = $request->getAttribute('email');
692
        $newEmail = $request->getAttribute('new-email');
693
        $token = $request->getAttribute('token');
694
        $message = null;
695
        $translator = $this->getTranslator();
696
697
        try {
698
699
            $link = $this->userService->findEmailLink($email, $token);
700
            $user = $link->getUser();
701
            $user->setEmail($newEmail);
702
            $this->userService->saveUser($user);
703
            $this->userService->deleteEmailLink($link);
704
            $message = [$translator->translate('email.changeemail.success', 'user') . $newEmail . $translator->translate('email.changeemail.success2', 'user'), 'success'];
705
            SessionManager::set('user', $user->getId());
0 ignored issues
show
Bug Best Practice introduced by
The method Del\SessionManager::set() is not static, but was called statically. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

705
            SessionManager::/** @scrutinizer ignore-call */ 
706
                            set('user', $user->getId());
Loading history...
706
707
        } catch (EmailLinkException $e) {
708
            $message = [$e->getMessage(), 'danger'];
709
        } catch (Exception $e) {
710
            throw $e;
711
        }
712
713
        $body = $this->getView()->render('boneuser::reset-email', ['message' => $message, 'logo' => $this->getLogo()]);
714
715
        return new HtmlResponse($body);
716
    }
717
}
718