User::accountPost()   F
last analyzed

Complexity

Conditions 19
Paths 254

Size

Total Lines 143
Code Lines 95

Duplication

Lines 66
Ratio 46.15 %

Importance

Changes 0
Metric Value
cc 19
eloc 95
nc 254
nop 1
dl 66
loc 143
rs 3.6524
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
3
namespace FFCMS\Controllers\User;
4
5
use FFMVC\Helpers;
6
use FFCMS\{Controllers, Models, Mappers, Traits, Enums};
7
8
/**
9
 * User Website Controller Class.
10
 *
11
 * @author Vijay Mahrra <[email protected]>
12
 * @copyright 2016 Vijay Mahrra
13
 * @license GPLv3 (http://www.gnu.org/licenses/gpl-3.0.html)
14
 */
15
class User extends Base
16
{
17
18
    /**
19
     * Add default scripts for displaying templates
20
     *
21
     * @return void
22
     * @see app/config/default.ini
23
     */
24
    protected function addScripts()
25
    {
26
        // no scripts to add, override me and set css and js
27
        $this->setScripts([], ['showdown']);
28
    }
29
30
31
    /**
32
     * user homepage
33
     *
34
     * @param \Base $f3
35
     * @return void
36
     */
37
    public function index(\Base $f3)
38
    {
39
        $this->redirectLoggedOutUser();
40
41
        $f3->set('form', $f3->get('REQUEST'));
42
        echo \View::instance()->render('user/index.phtml');
43
    }
44
45
46
    /**
47
     * login form submitted
48
     *
49
     * @param \Base $f3
50
     * @return void
51
     */
52
    public function loginPost(\Base $f3)
53
    {
54
        $this->redirectLoggedInUser();
55
        $this->csrf('@user');
56
57
        // url if login failed
58
        $view = 'user/login.phtml';
59
60
        // filter input vars of request
61
        $usersModel = Models\Users::instance();
62
        $usersMapper = $usersModel->getMapper();
63
        $usersMapper->copyfrom($f3->get('REQUEST'));
64
        $data = $usersMapper->filter();
65
        $request = $f3->get('REQUEST');
66
        foreach ($data as $k => $v) {
67
            if (array_key_exists($k, $request)) {
68
                $f3->set('REQUEST.' . $k, $v);
69
            }
70
        }
71
72
        // find user by email address
73
        $usersMapper = $usersModel->getUserByEmail($f3->get('REQUEST.email'));
74 View Code Duplication
        if (null == $usersMapper->id) {
75
            $this->notify(_('No user found with that email!'), 'error');
76
            $f3->set('form', $f3->get('REQUEST'));
77
            echo \View::instance()->render($view);
78
            return;
79
        }
80
81
        // check the password is set
82
        $password = $f3->get('REQUEST.password');
83 View Code Duplication
        if (empty($password)) {
84
            $this->notify(_('You must enter a password!'), 'warning');
85
            $f3->set('form', $f3->get('REQUEST'));
86
            echo \View::instance()->render($view);
87
            return;
88
        }
89
90
        // verify password
91 View Code Duplication
        if (!Helpers\Str::passwordVerify($usersMapper->password, $password)) {
92
            $this->notify(_('Incorrect password!'), 'warning');
93
            $f3->set('form', $f3->get('REQUEST'));
94
            echo \View::instance()->render($view);
95
            return;
96
        }
97
98
        if (!$usersModel->login()) {
99
            $this->notify(_('Unable to login!'), 'warning');
100
        } else {
101
            $f3->set('SESSION.uuid', $usersMapper->uuid);
102
            $f3->set('uuid', $usersMapper->uuid);
103
            $this->notify(_('You are now logged in!'), 'success');
104
            $uri = $f3->get('REQUEST.redirect_uri');
105
            return $f3->reroute(empty($uri) || !is_string($uri) ? '@user' : urldecode($uri));
106
        }
107
    }
108
109
110
    /**
111
     * my account
112
     *
113
     * @param \Base $f3
114
     * @return void
115
     */
116
    public function account(\Base $f3)
117
    {
118
        $this->redirectLoggedOutUser();
119
        $this->csrf();
120
121
        $f3->set('breadcrumbs', [
122
            _('My Account') => 'user',
123
            _('My Details') => 'account',
124
        ]);
125
126
        $f3->set('form', $f3->get('user'));
127
        echo \View::instance()->render('user/account.phtml');
128
    }
129
130
131
    /**
132
     * my account posted
133
     *
134
     * @param \Base $f3
135
     * @return void
136
     */
137
    public function accountPost(\Base $f3)
138
    {
139
        $this->redirectLoggedOutUser();
140
        $this->csrf();
141
142
        $view = 'user/account.phtml';
143
        $f3->set('breadcrumbs', [
144
            _('My Account') => 'user',
145
            _('My Details') => 'account',
146
        ]);
147
148
        // get current user details
149
        $usersModel = Models\Users::instance();
150
        $usersMapper = $usersModel->getUserByUUID($f3->get('uuid'));
151 View Code Duplication
        if (null == $usersMapper->id) {
152
            $this->notify(_('Your account no longer exists!'), 'error');
153
            $f3->set('form', $f3->get('REQUEST'));
154
            echo \View::instance()->render('user/account.phtml');
155
            return;
156
        }
157
158
        // check password is correct
159
        $str = Helpers\Str::instance();
160
        $old_password = $f3->get('REQUEST.old_password');
161 View Code Duplication
        if (empty($old_password) || !$str->passwordVerify($usersMapper->password, $old_password)) {
162
            $this->notify(_('You entered your current password incorrectly!'), 'warning');
163
            $f3->set('form', $f3->get('REQUEST'));
164
            echo \View::instance()->render($view);
165
            return;
166
        }
167
168
        // only allow updating of these fields
169
        $data = $f3->get('REQUEST');
170
        $fields = [
171
            'email',
172
            'password',
173
            'firstname',
174
            'lastname',
175
            'password_question',
176
            'password_answer',
177
        ];
178
        // check input data has values set for the above fields
179
        foreach ($fields as $k => $field) {
180
            if (!array_key_exists($field, $data)) {
181
                $data[$field] = null;
182
            }
183
        }
184
        // then remove any input data fields that aren't in the above fields
185
        foreach ($data as $field => $v) {
186
            if (!in_array($field, $fields)) {
187
                unset($data[$field]);
188
            }
189
        }
190
191
        // is this a password change?  if so, check they match
192
        $password = $f3->get('REQUEST.password');
193
        $confirm_password = $f3->get('REQUEST.confirm_password');
194 View Code Duplication
        if (!empty($password) || !empty($confirm_password)) {
195
            if ($password !== $confirm_password) {
196
                $this->notify(_('That password and confirm password must match!'), 'warning');
197
                $f3->set('form', $f3->get('REQUEST'));
198
                echo \View::instance()->render($view);
199
                return;
200
            } elseif ($str->passwordVerify($usersMapper->password, $password)) {
201
                $this->notify(_('The new password and old password are the same!'), 'warning');
202
                $f3->set('form', $f3->get('REQUEST'));
203
                echo \View::instance()->render($view);
204
                return;
205
            } else {
206
                // set new hashed password
207
                $data['password'] = $str->password($password);
208
            }
209
        } else {
210
            // same password
211
            $data['password'] = $usersMapper->password;
212
        }
213
214
        // check if email address change that email isn't taken
215
        $email = $f3->get('REQUEST.email');
216 View Code Duplication
        if ($usersMapper->email !== $email) {
217
            $usersMapper->load(['email = ?', $email]);
218
            if ($usersMapper->email == $email) {
219
                $this->notify(sprintf(_('The email address %s is already in use!'), $email), 'warning');
220
                $f3->set('form', $f3->get('REQUEST'));
221
                echo \View::instance()->render($view);
222
                return;
223
            } else {
224
                // new email
225
                $data['email'] = $email;
226
            }
227
        } else {
228
            // no change
229
            unset($data['email']);
230
        }
231
232
        // update required fields to check from ones which changed
233
        // validate the entered data
234
        $data['uuid'] = $f3->get('uuid');
235
        $usersMapper->copyfrom($data);
236
        $usersMapper->validationRequired($fields);
237
        $errors = $usersMapper->validate(false);
238 View Code Duplication
        if (is_array($errors)) {
239
            $this->notify(['warning' => $usersMapper->validationErrors($errors)]);
240
            $f3->set('form', $f3->get('REQUEST'));
241
            echo \View::instance()->render($view);
242
            return;
243
        }
244
245
        // no change, do nothing
246 View Code Duplication
        if (!$usersMapper->changed()) {
247
            $this->notify(_('There was nothing to change!'), 'info');
248
            $f3->set('form', $f3->get('REQUEST'));
249
            echo \View::instance()->render($view);
250
            return;
251
        }
252
253
        // reset usermapper and copy in valid data
254
        $usersMapper->load(['uuid = ?', $data['uuid']]);
255
        $usersMapper->copyfrom($data);
256 View Code Duplication
        if ($usersMapper->save()) {
257
            $this->notify(_('Your account was updated!'), 'success');
258
        } else {
259
            $this->notify(_('Unable to update your account!'), 'error');
260
            $f3->set('form', $f3->get('REQUEST'));
261
            echo \View::instance()->render($view);
262
            return;
263
        }
264
265
        // send verification email if email change - non-fatal
266
        if ($usersMapper->changed()) {
267
            // if email address changed, send confirmation enail
268
            if (!$usersModel->saveKey([
269
                'users_uuid' => $usersMapper->uuid,
270
                'key'       => 'email_confirmed',
271
                'value'     => 0
272
            ])) {
273
                $this->notify(_('Setting confirmation email failed.'), 'warning');
274
            }
275
            $this->sendConfirmationEmail();
276
        }
277
278
        $f3->reroute('@user');
279
    }
280
281
282
    /**
283
     * registration page
284
     *
285
     * @param \Base $f3
286
     * @return void
287
     */
288 View Code Duplication
    public function register(\Base $f3)
289
    {
290
        $this->redirectLoggedInUser();
291
        $this->csrf('@user');
292
293
        $f3->set('form', $f3->get('REQUEST'));
294
        echo \View::instance()->render('user/register.phtml');
295
    }
296
297
298
    /**
299
     * registration posted
300
     *
301
     * @param \Base $f3
302
     * @return void
303
     */
304
    public function registerPost(\Base $f3)
305
    {
306
        $this->redirectLoggedInUser();
307
        $this->csrf('@register');
308
309
        // filter input vars of request
310
        $usersModel = Models\Users::instance();
311
        $usersMapper = $usersModel->getMapper();
312
        $usersMapper->copyfrom($f3->get('REQUEST'));
313
        $data = $usersMapper->filter();
314
        $request = $f3->get('REQUEST');
315
        foreach ($data as $k => $v) {
316
            if (array_key_exists($k, $request)) {
317
                $f3->set('REQUEST.' . $k, $v);
318
            }
319
        }
320
321
        $view = 'user/register.phtml';
322
323
        $email = $f3->get('REQUEST.email');
324 View Code Duplication
        if (empty($email)) {
325
            $this->notify(_('You need to enter an email address!'), 'warning');
326
            $f3->set('form', $f3->get('REQUEST'));
327
            echo \View::instance()->render($view);
328
            return;
329
        }
330
331
        // find user by email address
332
        $usersModel = Models\Users::instance();
333
        $usersMapper = $usersModel->getUserByEmail($email);
334 View Code Duplication
        if (null !== $usersMapper->id) {
335
            $this->notify(_('That user already exists!'), 'error');
336
            $f3->set('form', $f3->get('REQUEST'));
337
            echo \View::instance()->render($view);
338
            return;
339
        }
340
341
        // bad password
342
        $password = $f3->get('REQUEST.password');
343
        $confirm_password = $f3->get('REQUEST.confirm_password');
344 View Code Duplication
        if (empty($password) || empty($confirm_password) || ($password !== $confirm_password)) {
345
            $this->notify(_('That password and confirm password must match!'), 'warning');
346
            $f3->set('form', $f3->get('REQUEST'));
347
            echo \View::instance()->render($view);
348
            return;
349
        }
350
351
        // use the model to validate the data input
352
        $usersMapper->copyfrom($f3->get('REQUEST'));
353
        $usersMapper->validationRequired([
354
            'email',
355
            'password',
356
            'firstname',
357
            'lastname',
358
            'password_question',
359
            'password_answer',
360
        ]);
361
362
        // set defaults
363
        $usersMapper->setUUID();
364
        $usersMapper->scopes = 'user';
365
        $usersMapper->status = 'registered';
366
        $usersMapper->created = Helpers\Time::database();
367
368
        $errors = $usersMapper->validate(false);
369
370 View Code Duplication
        if (is_array($errors)) {
371
            $this->notify(['info' => $usersMapper->validationErrors($errors)]);
372
            $f3->set('form', $f3->get('REQUEST'));
373
            echo \View::instance()->render($view);
374
            return;
375
        }
376
377 View Code Duplication
        if (!$usersModel->register()) {
378
            $this->notify(_('Registration failed!'), 'error');
379
            $f3->set('form', $f3->get('REQUEST'));
380
            echo \View::instance()->render($view);
381
            return;
382
        }
383
        $usersModel->login();
384
        $f3->set('SESSION.uuid', $usersMapper->uuid);
385
        $f3->set('uuid', $usersMapper->uuid);
386
        $this->notify(_('You successfully registered!'), 'success');
387
        $uri = $f3->get('REQUEST.redirect_uri');
388
389
        // send confirmation email
390
        $this->sendConfirmationEmail();
391
392
        return $f3->reroute(empty($uri) ? '@user' : urldecode($uri));
393
    }
394
395
396
    /**
397
     * Confirm email address link handler from email
398
     *
399
     * @param \Base $f3
400
     * @return void
401
     */
402
    public function confirmEmail(\Base $f3)
403
    {
404
        $usersModel = Models\Users::instance();
405
        $usersMapper = $usersModel->getMapper();
406
        $usersDataMapper = $usersModel->getDataMapper();
407
408
        // load in the forgot password reset code row
409
        $usersDataMapper->load([$usersDataMapper->quotekey('value')." = ? AND ".$usersDataMapper->quotekey('key')." = 'confirm_email_code'", $f3->get('REQUEST.code')]);
410 View Code Duplication
        if (null == $usersDataMapper->uuid) {
411
            $this->notify(_('Unknown password reset code!'), 'error');
412
            $f3->reroute('@index');
413
            return;
414
        }
415
416
        // check that the user exists for the reset code
417
        $usersMapper->load(['uuid = ?', $usersDataMapper->users_uuid]);
418 View Code Duplication
        if (null == $usersDataMapper->uuid) {
419
            $this->notify(_('Unknown user for confirmation code!'), 'error');
420
            $f3->reroute('@index');
421
            return;
422
        }
423
424
        // update account status to 'confirmed'
425
        $usersMapper->status = 'confirmed';
426
        if (!$usersMapper->save()) {
427
            $this->notify(_('Unable to update account status!'), 'error');
428
            $f3->reroute('@index');
429
            return;
430
        }
431
432
            //delete confirm_email_code and add email_confirmed
433
        $usersDataMapper->erase();
434
        $usersModel->saveKey([
435
            'users_uuid' => $usersMapper->uuid,
436
            'key'       => 'email_confirmed',
437
            'value'     => 1
438
        ]);
439
440
        $this->notify(_('Your email was successfully confirmed!'), 'success');
441
442
        $f3->reroute('@index');
443
    }
444
445
446
    /**
447
     * Send an email confirmation to the given user
448
     *
449
     * @param null|string $email the email address of the user
450
     * @param boolean true/false
451
     */
452
    private function sendConfirmationEmail(string $email = null)
453
    {
454
        $f3          = \Base::instance();
455
        $usersModel  = Models\Users::instance();
456
        $usersMapper = empty($email) ? $usersModel->getMapper() : $usersModel->getUserByEmail($email);
457
        $usersDataMapper = $usersModel->getDataMapper();
458
        if (empty($usersMapper->email)) {
459
            $this->notify(_('Could not send confirmation email.'), 'error');
460
            return;
461
        }
462
463
            // generate a random code to email to the user for confirming the email
464
        $usersModel->saveKey([
465
            'users_uuid' => $usersMapper->uuid,
466
            'key' => 'confirm_email_code',
467
            'value' => Helpers\Str::random(6)
468
        ]);
469
470
            // set the email template variables
471
        $f3->set('templateData',
472
            array_merge($usersMapper->cast(), [
473
            'code' => $usersDataMapper->value,
474
            'url' => Helpers\Url::internal('@confirm_email')
475
        ]));
476
477
        // setup PHPMailer
478
        $mail = Helpers\Mail::getPHPMailer([
479
            'To'      => $usersMapper->email,
480
            'Subject' => "Confirm Email Address",
481
            'Body'    => \Markdown::instance()->convert(\View::instance()->render('email/confirm_email.md')),
482
            'AltBody' => \View::instance()->render('email/forgot_password.md', 'text/plain')
483
        ]);
484
485 View Code Duplication
        if ($mail->send()) {
486
            $this->notify(_("A notification has been sent to confirm your email address."), "success");
487
        } else {
488
            $this->notify(_("There was a problem sending you a registration email, please check your email and/or try again later."), "warning");
489
            $this->notify($mail->ErrorInfo, "error");
490
        }
491
    }
492
493
494
    /**
495
     * my profile
496
     *
497
     * @param \Base $f3
498
     * @return void
499
     */
500 View Code Duplication
    public function profile(\Base $f3)
501
    {
502
        $this->redirectLoggedOutUser();
503
        $this->csrf();
504
505
        $f3->set('breadcrumbs', [
506
            _('My Account') => 'user',
507
            _('My Profile') => 'profile',
508
        ]);
509
510
        // fetch profile
511
        $usersModel = Models\Users::instance();
512
        $profileData = $usersModel->getProfile($f3->get('uuid'));
513
        $f3->set('form', $profileData);
514
515
        echo \View::instance()->render('user/profile.phtml');
516
    }
517
518
519
    /**
520
     * my profile posted
521
     *
522
     * @param \Base $f3
523
     * @return void
524
     */
525
    public function profilePost(\Base $f3)
526
    {
527
        $this->redirectLoggedOutUser();
528
        $this->csrf();
529
530
        $view = 'user/profile.phtml';
531
        $f3->set('breadcrumbs', [
532
            _('My Account') => 'user',
533
            _('My Profile') => 'profile',
534
        ]);
535
536
        // handle file upload
537
        // wrong upload form field name or
538
        // upload mime type is not image/* or
539
        // size > 4 MB upload limit
540
        $files = \Web::instance()->receive(function($metadata, $fieldname){
541
            return !(
542
                'profile' !== $fieldname ||
543
                'image/' !== substr($metadata['type'], 0, 6) ||
544
                $metadata['size'] > Enums\Bytes::MEGABYTE() * 4
545
            );
546
        }, true, true);
547
548
        // create new profile image
549
        if (is_array($files)) {
550
            foreach ($files as $file => $valid) {
551
                if (false === $valid) {
552
                    $this->notify(_("The file uploaded was not valid!"), 'error');
553
                } else {
554
                    $user = $f3->get('usersMapper');
555
                    if ($user->profileImageCreate($file)) {
556
                        $this->notify(_("Your profile picture was updated!"), 'success');
557
                    }
558
                }
559
                unlink($file);
560
            }
561
        }
562
563
        // get existing profile and merge with input
564
        $usersModel = Models\Users::instance();
565
        $profileEnum = new Enums\ProfileKeys;
566
567
        // merge profile keys and filter input
568
        $profileData = $this->filter(
569
            array_intersect_key(
570
                array_merge(
571
                    $usersModel->getProfile($f3->get('uuid')),
572
                    $f3->get('REQUEST')
573
                ),
574
                $profileEnum->values()
575
            ), [
576
            'nickname' => 'trim|sanitize_string',
577
            'bio' => 'trim|sanitize_string'
578
        ]);
579
580
        $errors = $this->validate(false, $profileData, [
581
            'nickname' => 'valid_name',
582
        ]);
583 View Code Duplication
        if (is_array($errors)) {
584
            $this->notify(['warning' => $this->validationErrors($errors)]);
585
            $f3->set('form', $f3->get('REQUEST'));
586
            echo \View::instance()->render($view);
587
            return;
588
        }
589
590
        // save profile
591
        $usersModel->saveData($f3->get('uuid'), $profileData);
592
593
        // set form data
594
        $f3->set('form', $profileData);
595
596
        echo \View::instance()->render('user/profile.phtml');
597
    }
598
}
599