Failed Conditions
Push — bootstrap4 ( d74498...ef1861 )
by Simon
06:56
created

User::setWelcomeSig()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 1
1
<?php
2
/******************************************************************************
3
 * Wikipedia Account Creation Assistance tool                                 *
4
 *                                                                            *
5
 * All code in this file is released into the public domain by the ACC        *
6
 * Development Team. Please see team.json for a list of contributors.         *
7
 ******************************************************************************/
8
9
namespace Waca\DataObjects;
10
11
use DateTime;
12
use Exception;
13
use Waca\DataObject;
14
use Waca\Exceptions\OptimisticLockFailedException;
15
use Waca\IdentificationVerifier;
16
use Waca\PdoDatabase;
17
use Waca\WebRequest;
18
19
/**
20
 * User data object
21
 */
22
class User extends DataObject
23
{
24
    const STATUS_ACTIVE = 'Active';
25
    const STATUS_SUSPENDED = 'Suspended';
26
    const STATUS_DECLINED = 'Declined';
27
    const STATUS_NEW = 'New';
28
    const CREATION_MANUAL = 0;
29
    const CREATION_OAUTH = 1;
30
    const CREATION_BOT = 2;
31
    private $username;
32
    private $email;
33
    private $status = self::STATUS_NEW;
34
    private $onwikiname;
35
    private $welcome_sig = "";
36
    private $lastactive = "0000-00-00 00:00:00";
37
    private $forcelogout = 0;
38
    private $forceidentified = null;
39
    private $welcome_template = 0;
40
    private $abortpref = 0;
41
    private $confirmationdiff = 0;
42
    private $emailsig = "";
43
    private $creationmode = 0;
44
    /** @var User Cache variable of the current user - it's never going to change in the middle of a request. */
45
    private static $currentUser;
46
    #region Object load methods
47
48
    /**
49
     * Gets the currently logged in user
50
     *
51
     * @param PdoDatabase $database
52
     *
53
     * @return User|CommunityUser
54
     */
55
    public static function getCurrent(PdoDatabase $database)
56
    {
57
        if (self::$currentUser === null) {
58
            $sessionId = WebRequest::getSessionUserId();
59
60
            if ($sessionId !== null) {
61
                /** @var User $user */
62
                $user = self::getById($sessionId, $database);
63
64
                if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
65
                    self::$currentUser = new CommunityUser();
66
                }
67
                else {
68
                    self::$currentUser = $user;
69
                }
70
            }
71
            else {
72
                $anonymousCoward = new CommunityUser();
73
74
                self::$currentUser = $anonymousCoward;
75
            }
76
        }
77
78
        return self::$currentUser;
79
    }
80
81
    /**
82
     * Gets a user by their user ID
83
     *
84
     * Pass -1 to get the community user.
85
     *
86
     * @param int|null    $id
87
     * @param PdoDatabase $database
88
     *
89
     * @return User|false
90
     */
91
    public static function getById($id, PdoDatabase $database)
92
    {
93
        if ($id === null || $id == -1) {
94
            return new CommunityUser();
95
        }
96
97
        /** @var User|false $user */
98
        $user = parent::getById($id, $database);
99
100
        return $user;
101
    }
102
103
    /**
104
     * @return CommunityUser
105
     */
106
    public static function getCommunity()
107
    {
108
        return new CommunityUser();
109
    }
110
111
    /**
112
     * Gets a user by their username
113
     *
114
     * @param  string      $username
115
     * @param  PdoDatabase $database
116
     *
117
     * @return CommunityUser|User|false
118
     */
119
    public static function getByUsername($username, PdoDatabase $database)
120
    {
121
        global $communityUsername;
122
        if ($username == $communityUsername) {
123
            return new CommunityUser();
124
        }
125
126
        $statement = $database->prepare("SELECT * FROM user WHERE username = :id LIMIT 1;");
127
        $statement->bindValue(":id", $username);
128
129
        $statement->execute();
130
131
        $resultObject = $statement->fetchObject(get_called_class());
132
133
        if ($resultObject != false) {
134
            $resultObject->setDatabase($database);
135
        }
136
137
        return $resultObject;
138
    }
139
140
    /**
141
     * Gets a user by their on-wiki username.
142
     *
143
     * @param string      $username
144
     * @param PdoDatabase $database
145
     *
146
     * @return User|false
147
     */
148
    public static function getByOnWikiUsername($username, PdoDatabase $database)
149
    {
150
        $statement = $database->prepare("SELECT * FROM user WHERE onwikiname = :id LIMIT 1;");
151
        $statement->bindValue(":id", $username);
152
        $statement->execute();
153
154
        $resultObject = $statement->fetchObject(get_called_class());
155
156
        if ($resultObject != false) {
157
            $resultObject->setDatabase($database);
158
159
            return $resultObject;
160
        }
161
162
        return false;
163
    }
164
165
    #endregion
166
167
    /**
168
     * Saves the current object
169
     *
170
     * @throws Exception
171
     */
172
    public function save()
173
    {
174
        if ($this->isNew()) {
175
            // insert
176
            $statement = $this->dbObject->prepare(<<<SQL
177
				INSERT INTO `user` ( 
178
					username, email, status, onwikiname, welcome_sig, 
179
					lastactive, forcelogout, forceidentified,
180
					welcome_template, abortpref, confirmationdiff, emailsig, creationmode
181
				) VALUES (
182
					:username, :email, :status, :onwikiname, :welcome_sig,
183
					:lastactive, :forcelogout, :forceidentified,
184
					:welcome_template, :abortpref, :confirmationdiff, :emailsig, :creationmode
185
				);
186
SQL
187
            );
188
            $statement->bindValue(":username", $this->username);
189
            $statement->bindValue(":email", $this->email);
190
            $statement->bindValue(":status", $this->status);
191
            $statement->bindValue(":onwikiname", $this->onwikiname);
192
            $statement->bindValue(":welcome_sig", $this->welcome_sig);
193
            $statement->bindValue(":lastactive", $this->lastactive);
194
            $statement->bindValue(":forcelogout", $this->forcelogout);
195
            $statement->bindValue(":forceidentified", $this->forceidentified);
196
            $statement->bindValue(":welcome_template", $this->welcome_template);
197
            $statement->bindValue(":abortpref", $this->abortpref);
198
            $statement->bindValue(":confirmationdiff", $this->confirmationdiff);
199
            $statement->bindValue(":emailsig", $this->emailsig);
200
            $statement->bindValue(":creationmode", $this->creationmode);
201
202
            if ($statement->execute()) {
203
                $this->id = (int)$this->dbObject->lastInsertId();
204
            }
205
            else {
206
                throw new Exception($statement->errorInfo());
0 ignored issues
show
Bug introduced by
$statement->errorInfo() of type array is incompatible with the type string expected by parameter $message of Exception::__construct(). ( Ignorable by Annotation )

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

206
                throw new Exception(/** @scrutinizer ignore-type */ $statement->errorInfo());
Loading history...
207
            }
208
        }
209
        else {
210
            // update
211
            $statement = $this->dbObject->prepare(<<<SQL
212
				UPDATE `user` SET 
213
					username = :username, email = :email, 
214
					status = :status,
215
					onwikiname = :onwikiname, welcome_sig = :welcome_sig, 
216
					lastactive = :lastactive, forcelogout = :forcelogout, 
217
					forceidentified = :forceidentified,
218
					welcome_template = :welcome_template, abortpref = :abortpref, 
219
					confirmationdiff = :confirmationdiff, emailsig = :emailsig, 
220
					creationmode = :creationmode, updateversion = updateversion + 1
221
				WHERE id = :id AND updateversion = :updateversion;
222
SQL
223
            );
224
            $statement->bindValue(":forceidentified", $this->forceidentified);
225
226
            $statement->bindValue(':id', $this->id);
227
            $statement->bindValue(':updateversion', $this->updateversion);
228
229
            $statement->bindValue(':username', $this->username);
230
            $statement->bindValue(':email', $this->email);
231
            $statement->bindValue(':status', $this->status);
232
            $statement->bindValue(':onwikiname', $this->onwikiname);
233
            $statement->bindValue(':welcome_sig', $this->welcome_sig);
234
            $statement->bindValue(':lastactive', $this->lastactive);
235
            $statement->bindValue(':forcelogout', $this->forcelogout);
236
            $statement->bindValue(':forceidentified', $this->forceidentified);
237
            $statement->bindValue(':welcome_template', $this->welcome_template);
238
            $statement->bindValue(':abortpref', $this->abortpref);
239
            $statement->bindValue(':confirmationdiff', $this->confirmationdiff);
240
            $statement->bindValue(':emailsig', $this->emailsig);
241
            $statement->bindValue(':creationmode', $this->creationmode);
242
243
            if (!$statement->execute()) {
244
                throw new Exception($statement->errorInfo());
245
            }
246
247
            if ($statement->rowCount() !== 1) {
248
                throw new OptimisticLockFailedException();
249
            }
250
251
            $this->updateversion++;
252
        }
253
    }
254
255
    #region properties
256
257
    /**
258
     * Gets the tool username
259
     * @return string
260
     */
261
    public function getUsername()
262
    {
263
        return $this->username;
264
    }
265
266
    /**
267
     * Sets the tool username
268
     *
269
     * @param string $username
270
     */
271
    public function setUsername($username)
272
    {
273
        $this->username = $username;
274
275
        // If this isn't a brand new user, then it's a rename, force the logout
276
        if (!$this->isNew()) {
277
            $this->forcelogout = 1;
278
        }
279
    }
280
281
    /**
282
     * Gets the user's email address
283
     * @return string
284
     */
285
    public function getEmail()
286
    {
287
        return $this->email;
288
    }
289
290
    /**
291
     * Sets the user's email address
292
     *
293
     * @param string $email
294
     */
295
    public function setEmail($email)
296
    {
297
        $this->email = $email;
298
    }
299
300
    /**
301
     * Gets the status (User, Admin, Suspended, etc - excludes checkuser) of the user.
302
     * @return string
303
     */
304
    public function getStatus()
305
    {
306
        return $this->status;
307
    }
308
309
    /**
310
     * @param string $status
311
     */
312
    public function setStatus($status)
313
    {
314
        $this->status = $status;
315
    }
316
317
    /**
318
     * Gets the user's on-wiki name
319
     * @return string
320
     */
321
    public function getOnWikiName()
322
    {
323
        return $this->onwikiname;
324
    }
325
326
    /**
327
     * Sets the user's on-wiki name
328
     *
329
     * This can have interesting side-effects with OAuth.
330
     *
331
     * @param string $onWikiName
332
     */
333
    public function setOnWikiName($onWikiName)
334
    {
335
        $this->onwikiname = $onWikiName;
336
    }
337
338
    /**
339
     * Gets the welcome signature
340
     * @return string
341
     */
342
    public function getWelcomeSig()
343
    {
344
        return $this->welcome_sig;
345
    }
346
347
    /**
348
     * Sets the welcome signature
349
     *
350
     * @param string $welcomeSig
351
     */
352
    public function setWelcomeSig($welcomeSig)
353
    {
354
        $this->welcome_sig = $welcomeSig;
355
    }
356
357
    /**
358
     * Gets the last activity date for the user
359
     *
360
     * @return string
361
     * @todo This should probably return an instance of DateTime
362
     */
363
    public function getLastActive()
364
    {
365
        return $this->lastactive;
366
    }
367
368
    /**
369
     * Gets the user's forced logout status
370
     *
371
     * @return bool
372
     */
373
    public function getForceLogout()
374
    {
375
        return $this->forcelogout == 1;
376
    }
377
378
    /**
379
     * Sets the user's forced logout status
380
     *
381
     * @param bool $forceLogout
382
     */
383
    public function setForceLogout($forceLogout)
384
    {
385
        $this->forcelogout = $forceLogout ? 1 : 0;
386
    }
387
388
    /**
389
     * Returns the ID of the welcome template used.
390
     * @return int
391
     */
392
    public function getWelcomeTemplate()
393
    {
394
        return $this->welcome_template;
395
    }
396
397
    /**
398
     * Sets the ID of the welcome template used.
399
     *
400
     * @param int $welcomeTemplate
401
     */
402
    public function setWelcomeTemplate($welcomeTemplate)
403
    {
404
        $this->welcome_template = $welcomeTemplate;
405
    }
406
407
    /**
408
     * Gets the user's abort preference
409
     * @todo this is badly named too! Also a bool that's actually an int.
410
     * @return int
411
     */
412
    public function getAbortPref()
413
    {
414
        return $this->abortpref;
415
    }
416
417
    /**
418
     * Sets the user's abort preference
419
     * @todo rename, retype, and re-comment.
420
     *
421
     * @param int $abortPreference
422
     */
423
    public function setAbortPref($abortPreference)
424
    {
425
        $this->abortpref = $abortPreference;
426
    }
427
428
    /**
429
     * Gets the user's confirmation diff. Unused if OAuth is in use.
430
     * @return int the diff ID
431
     */
432
    public function getConfirmationDiff()
433
    {
434
        return $this->confirmationdiff;
435
    }
436
437
    /**
438
     * Sets the user's confirmation diff.
439
     *
440
     * @param int $confirmationDiff
441
     */
442
    public function setConfirmationDiff($confirmationDiff)
443
    {
444
        $this->confirmationdiff = $confirmationDiff;
445
    }
446
447
    /**
448
     * Gets the users' email signature used on outbound mail.
449
     * @todo rename me!
450
     * @return string
451
     */
452
    public function getEmailSig()
453
    {
454
        return $this->emailsig;
455
    }
456
457
    /**
458
     * Sets the user's email signature for outbound mail.
459
     *
460
     * @param string $emailSignature
461
     */
462
    public function setEmailSig($emailSignature)
463
    {
464
        $this->emailsig = $emailSignature;
465
    }
466
467
    /**
468
     * @return int
469
     */
470
    public function getCreationMode()
471
    {
472
        return $this->creationmode;
473
    }
474
475
    /**
476
     * @param $creationMode int
477
     */
478
    public function setCreationMode($creationMode)
479
    {
480
        $this->creationmode = $creationMode;
481
    }
482
483
    #endregion
484
485
    #region user access checks
486
487
    public function isActive()
488
    {
489
        return $this->status == self::STATUS_ACTIVE;
490
    }
491
492
    /**
493
     * Tests if the user is identified
494
     *
495
     * @param IdentificationVerifier $iv
496
     *
497
     * @return bool
498
     * @todo     Figure out what on earth is going on with PDO's typecasting here.  Apparently, it returns string("0") for
499
     *       the force-unidentified case, and int(1) for the identified case?!  This is quite ugly, but probably needed
500
     *       to play it safe for now.
501
     * @category Security-Critical
502
     */
503
    public function isIdentified(IdentificationVerifier $iv)
504
    {
505
        if ($this->forceidentified === 0 || $this->forceidentified === "0") {
506
            // User forced to unidentified in the database.
507
            return false;
508
        }
509
        elseif ($this->forceidentified === 1 || $this->forceidentified === "1") {
510
            // User forced to identified in the database.
511
            return true;
512
        }
513
        else {
514
            // User not forced to any particular identified status; consult IdentificationVerifier
515
            return $iv->isUserIdentified($this->getOnWikiName());
516
        }
517
    }
518
519
    /**
520
     * Tests if the user is suspended
521
     * @return bool
522
     * @category Security-Critical
523
     */
524
    public function isSuspended()
525
    {
526
        return $this->status == self::STATUS_SUSPENDED;
527
    }
528
529
    /**
530
     * Tests if the user is new
531
     * @return bool
532
     * @category Security-Critical
533
     */
534
    public function isNewUser()
535
    {
536
        return $this->status == self::STATUS_NEW;
537
    }
538
539
    /**
540
     * Tests if the user has been declined access to the tool
541
     * @return bool
542
     * @category Security-Critical
543
     */
544
    public function isDeclined()
545
    {
546
        return $this->status == self::STATUS_DECLINED;
547
    }
548
549
    /**
550
     * Tests if the user is the community user
551
     *
552
     * @todo     decide if this means logged out. I think it usually does.
553
     * @return bool
554
     * @category Security-Critical
555
     */
556
    public function isCommunityUser()
557
    {
558
        return false;
559
    }
560
561
    #endregion 
562
563
    /**
564
     * Gets a hash of data for the user to reset their password with.
565
     * @category Security-Critical
566
     * @return string
567
     */
568
    public function getForgottenPasswordHash()
569
    {
570
        // FIXME
571
        return md5($this->username . $this->email . $this->welcome_template . $this->id);
572
    }
573
574
    /**
575
     * Gets the approval date of the user
576
     * @return DateTime|false
577
     */
578
    public function getApprovalDate()
579
    {
580
        $query = $this->dbObject->prepare(<<<SQL
581
			SELECT timestamp 
582
			FROM log 
583
			WHERE objectid = :userid
584
				AND objecttype = 'User'
585
				AND action = 'Approved' 
586
			ORDER BY id DESC 
587
			LIMIT 1;
588
SQL
589
        );
590
        $query->execute(array(":userid" => $this->id));
591
592
        $data = DateTime::createFromFormat("Y-m-d H:i:s", $query->fetchColumn());
593
        $query->closeCursor();
594
595
        return $data;
596
    }
597
}
598