Completed
Pull Request — newinternal (#542)
by Simon
11:55 queued 02:00
created

User::setEmail()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 1
dl 0
loc 3
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 1
crap 2
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
    private $skin = "main";
45
    /** @var User Cache variable of the current user - it's never going to change in the middle of a request. */
46
    private static $currentUser;
47
    #region Object load methods
48
49
    /**
50
     * Gets the currently logged in user
51
     *
52
     * @param PdoDatabase $database
53
     *
54
     * @return User|CommunityUser
55
     */
56
    public static function getCurrent(PdoDatabase $database)
57
    {
58
        if (self::$currentUser === null) {
59
            $sessionId = WebRequest::getSessionUserId();
60
61
            if ($sessionId !== null) {
62
                /** @var User $user */
63
                $user = self::getById($sessionId, $database);
64
65
                if ($user === false) {
0 ignored issues
show
introduced by
The condition $user === false is always false.
Loading history...
66
                    self::$currentUser = new CommunityUser();
67
                }
68
                else {
69
                    self::$currentUser = $user;
70
                }
71
            }
72
            else {
73
                $anonymousCoward = new CommunityUser();
74
75
                self::$currentUser = $anonymousCoward;
76
            }
77
        }
78
79
        return self::$currentUser;
80
    }
81
82
    /**
83
     * Gets a user by their user ID
84
     *
85
     * Pass -1 to get the community user.
86
     *
87
     * @param int|null    $id
88
     * @param PdoDatabase $database
89
     *
90
     * @return User|false
91
     */
92
    public static function getById($id, PdoDatabase $database)
93
    {
94
        if ($id === null || $id == -1) {
95
            return new CommunityUser();
96
        }
97
98
        /** @var User|false $user */
99
        $user = parent::getById($id, $database);
100
101
        return $user;
102
    }
103
104
    /**
105
     * @return CommunityUser
106
     */
107
    public static function getCommunity()
108
    {
109
        return new CommunityUser();
110
    }
111
112
    /**
113
     * Gets a user by their username
114
     *
115
     * @param  string      $username
116
     * @param  PdoDatabase $database
117
     *
118
     * @return CommunityUser|User|false
119
     */
120
    public static function getByUsername($username, PdoDatabase $database)
121
    {
122
        global $communityUsername;
123
        if ($username == $communityUsername) {
124
            return new CommunityUser();
125
        }
126
127
        $statement = $database->prepare("SELECT * FROM user WHERE username = :id LIMIT 1;");
128
        $statement->bindValue(":id", $username);
129
130
        $statement->execute();
131
132
        $resultObject = $statement->fetchObject(get_called_class());
133
134
        if ($resultObject != false) {
135
            $resultObject->setDatabase($database);
136
        }
137
138
        return $resultObject;
139
    }
140
141
    /**
142
     * Gets a user by their on-wiki username.
143
     *
144
     * @param string      $username
145
     * @param PdoDatabase $database
146
     *
147
     * @return User|false
148
     */
149
    public static function getByOnWikiUsername($username, PdoDatabase $database)
150
    {
151
        $statement = $database->prepare("SELECT * FROM user WHERE onwikiname = :id LIMIT 1;");
152
        $statement->bindValue(":id", $username);
153
        $statement->execute();
154
155
        $resultObject = $statement->fetchObject(get_called_class());
156
157
        if ($resultObject != false) {
158
            $resultObject->setDatabase($database);
159
160
            return $resultObject;
161
        }
162
163
        return false;
164
    }
165
166
    #endregion
167
168
    /**
169
     * Saves the current object
170
     *
171
     * @throws Exception
172
     */
173
    public function save()
174
    {
175
        if ($this->isNew()) {
176
            // insert
177
            $statement = $this->dbObject->prepare(<<<SQL
178
				INSERT INTO `user` ( 
179
					username, email, status, onwikiname, welcome_sig, 
180
					lastactive, forcelogout, forceidentified,
181
					welcome_template, abortpref, confirmationdiff, emailsig, creationmode, skin
182
				) VALUES (
183
					:username, :email, :status, :onwikiname, :welcome_sig,
184
					:lastactive, :forcelogout, :forceidentified,
185
					:welcome_template, :abortpref, :confirmationdiff, :emailsig, :creationmode, :skin
186
				);
187
SQL
188
            );
189
            $statement->bindValue(":username", $this->username);
190
            $statement->bindValue(":email", $this->email);
191
            $statement->bindValue(":status", $this->status);
192
            $statement->bindValue(":onwikiname", $this->onwikiname);
193
            $statement->bindValue(":welcome_sig", $this->welcome_sig);
194
            $statement->bindValue(":lastactive", $this->lastactive);
195
            $statement->bindValue(":forcelogout", $this->forcelogout);
196
            $statement->bindValue(":forceidentified", $this->forceidentified);
197
            $statement->bindValue(":welcome_template", $this->welcome_template);
198
            $statement->bindValue(":abortpref", $this->abortpref);
199
            $statement->bindValue(":confirmationdiff", $this->confirmationdiff);
200
            $statement->bindValue(":emailsig", $this->emailsig);
201
            $statement->bindValue(":creationmode", $this->creationmode);
202
            $statement->bindValue(":skin", $this->skin);
203
204
            if ($statement->execute()) {
205
                $this->id = (int)$this->dbObject->lastInsertId();
206
            }
207
            else {
208
                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

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