Completed
Pull Request — newinternal (#285)
by Simon
06:16 queued 03:05
created
includes/Helpers/SearchHelpers/RequestSearchHelper.php 1 patch
Indentation   +113 added lines, -113 removed lines patch added patch discarded remove patch
@@ -14,117 +14,117 @@
 block discarded – undo
14 14
 
15 15
 class RequestSearchHelper extends SearchHelperBase
16 16
 {
17
-    /**
18
-     * RequestSearchHelper constructor.
19
-     *
20
-     * @param PdoDatabase $database
21
-     */
22
-    protected function __construct(PdoDatabase $database)
23
-    {
24
-        parent::__construct($database, 'request', Request::class);
25
-    }
26
-
27
-    /**
28
-     * Initiates a search for requests
29
-     *
30
-     * @param PdoDatabase $database
31
-     *
32
-     * @return RequestSearchHelper
33
-     */
34
-    public static function get(PdoDatabase $database)
35
-    {
36
-        $helper = new RequestSearchHelper($database);
37
-
38
-        return $helper;
39
-    }
40
-
41
-    /**
42
-     * Filters the results by IP address
43
-     *
44
-     * @param string $ipAddress
45
-     *
46
-     * @return $this
47
-     */
48
-    public function byIp($ipAddress)
49
-    {
50
-        $this->whereClause .= ' AND (ip LIKE ? OR forwardedip LIKE ?)';
51
-        $this->parameterList[] = $ipAddress;
52
-        $this->parameterList[] = '%' . trim($ipAddress, '%') . '%';
53
-
54
-        return $this;
55
-    }
56
-
57
-    /**
58
-     * Filters the results by email address
59
-     *
60
-     * @param string $emailAddress
61
-     *
62
-     * @return $this
63
-     */
64
-    public function byEmailAddress($emailAddress)
65
-    {
66
-        $this->whereClause .= ' AND email LIKE ?';
67
-        $this->parameterList[] = $emailAddress;
68
-
69
-        return $this;
70
-    }
71
-
72
-    /**
73
-     * Filters the results by name
74
-     *
75
-     * @param string $name
76
-     *
77
-     * @return $this
78
-     */
79
-    public function byName($name)
80
-    {
81
-        $this->whereClause .= ' AND name LIKE ?';
82
-        $this->parameterList[] = $name;
83
-
84
-        return $this;
85
-    }
86
-
87
-    /**
88
-     * Excludes a request from the results
89
-     *
90
-     * @param int $requestId
91
-     *
92
-     * @return $this
93
-     */
94
-    public function excludingRequest($requestId)
95
-    {
96
-        $this->whereClause .= ' AND id <> ?';
97
-        $this->parameterList[] = $requestId;
98
-
99
-        return $this;
100
-    }
101
-
102
-    /**
103
-     * Filters the results to only those with a confirmed email address
104
-     *
105
-     * @return $this
106
-     */
107
-    public function withConfirmedEmail()
108
-    {
109
-        $this->whereClause .= ' AND emailconfirm = ?';
110
-        $this->parameterList[] = 'Confirmed';
111
-
112
-        return $this;
113
-    }
114
-
115
-    /**
116
-     * Filters the results to exclude purged data
117
-     *
118
-     * @param SiteConfiguration $configuration
119
-     *
120
-     * @return $this
121
-     */
122
-    public function excludingPurgedData(SiteConfiguration $configuration)
123
-    {
124
-        $this->whereClause .= ' AND ip <> ? AND email <> ?';
125
-        $this->parameterList[] = $configuration->getDataClearIp();
126
-        $this->parameterList[] = $configuration->getDataClearEmail();
127
-
128
-        return $this;
129
-    }
17
+	/**
18
+	 * RequestSearchHelper constructor.
19
+	 *
20
+	 * @param PdoDatabase $database
21
+	 */
22
+	protected function __construct(PdoDatabase $database)
23
+	{
24
+		parent::__construct($database, 'request', Request::class);
25
+	}
26
+
27
+	/**
28
+	 * Initiates a search for requests
29
+	 *
30
+	 * @param PdoDatabase $database
31
+	 *
32
+	 * @return RequestSearchHelper
33
+	 */
34
+	public static function get(PdoDatabase $database)
35
+	{
36
+		$helper = new RequestSearchHelper($database);
37
+
38
+		return $helper;
39
+	}
40
+
41
+	/**
42
+	 * Filters the results by IP address
43
+	 *
44
+	 * @param string $ipAddress
45
+	 *
46
+	 * @return $this
47
+	 */
48
+	public function byIp($ipAddress)
49
+	{
50
+		$this->whereClause .= ' AND (ip LIKE ? OR forwardedip LIKE ?)';
51
+		$this->parameterList[] = $ipAddress;
52
+		$this->parameterList[] = '%' . trim($ipAddress, '%') . '%';
53
+
54
+		return $this;
55
+	}
56
+
57
+	/**
58
+	 * Filters the results by email address
59
+	 *
60
+	 * @param string $emailAddress
61
+	 *
62
+	 * @return $this
63
+	 */
64
+	public function byEmailAddress($emailAddress)
65
+	{
66
+		$this->whereClause .= ' AND email LIKE ?';
67
+		$this->parameterList[] = $emailAddress;
68
+
69
+		return $this;
70
+	}
71
+
72
+	/**
73
+	 * Filters the results by name
74
+	 *
75
+	 * @param string $name
76
+	 *
77
+	 * @return $this
78
+	 */
79
+	public function byName($name)
80
+	{
81
+		$this->whereClause .= ' AND name LIKE ?';
82
+		$this->parameterList[] = $name;
83
+
84
+		return $this;
85
+	}
86
+
87
+	/**
88
+	 * Excludes a request from the results
89
+	 *
90
+	 * @param int $requestId
91
+	 *
92
+	 * @return $this
93
+	 */
94
+	public function excludingRequest($requestId)
95
+	{
96
+		$this->whereClause .= ' AND id <> ?';
97
+		$this->parameterList[] = $requestId;
98
+
99
+		return $this;
100
+	}
101
+
102
+	/**
103
+	 * Filters the results to only those with a confirmed email address
104
+	 *
105
+	 * @return $this
106
+	 */
107
+	public function withConfirmedEmail()
108
+	{
109
+		$this->whereClause .= ' AND emailconfirm = ?';
110
+		$this->parameterList[] = 'Confirmed';
111
+
112
+		return $this;
113
+	}
114
+
115
+	/**
116
+	 * Filters the results to exclude purged data
117
+	 *
118
+	 * @param SiteConfiguration $configuration
119
+	 *
120
+	 * @return $this
121
+	 */
122
+	public function excludingPurgedData(SiteConfiguration $configuration)
123
+	{
124
+		$this->whereClause .= ' AND ip <> ? AND email <> ?';
125
+		$this->parameterList[] = $configuration->getDataClearIp();
126
+		$this->parameterList[] = $configuration->getDataClearEmail();
127
+
128
+		return $this;
129
+	}
130 130
 }
131 131
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Helpers/SearchHelpers/LogSearchHelper.php 1 patch
Indentation   +73 added lines, -73 removed lines patch added patch discarded remove patch
@@ -13,87 +13,87 @@
 block discarded – undo
13 13
 
14 14
 class LogSearchHelper extends SearchHelperBase
15 15
 {
16
-    /**
17
-     * LogSearchHelper constructor.
18
-     *
19
-     * @param PdoDatabase $database
20
-     */
21
-    protected function __construct(PdoDatabase $database)
22
-    {
23
-        parent::__construct($database, 'log', Log::class, 'timestamp DESC');
24
-    }
16
+	/**
17
+	 * LogSearchHelper constructor.
18
+	 *
19
+	 * @param PdoDatabase $database
20
+	 */
21
+	protected function __construct(PdoDatabase $database)
22
+	{
23
+		parent::__construct($database, 'log', Log::class, 'timestamp DESC');
24
+	}
25 25
 
26
-    /**
27
-     * Initiates a search for requests
28
-     *
29
-     * @param PdoDatabase $database
30
-     *
31
-     * @return LogSearchHelper
32
-     */
33
-    public static function get(PdoDatabase $database)
34
-    {
35
-        $helper = new LogSearchHelper($database);
26
+	/**
27
+	 * Initiates a search for requests
28
+	 *
29
+	 * @param PdoDatabase $database
30
+	 *
31
+	 * @return LogSearchHelper
32
+	 */
33
+	public static function get(PdoDatabase $database)
34
+	{
35
+		$helper = new LogSearchHelper($database);
36 36
 
37
-        return $helper;
38
-    }
37
+		return $helper;
38
+	}
39 39
 
40
-    /**
41
-     * Filters the results by user
42
-     *
43
-     * @param int $userId
44
-     *
45
-     * @return $this
46
-     */
47
-    public function byUser($userId)
48
-    {
49
-        $this->whereClause .= ' AND user = ?';
50
-        $this->parameterList[] = $userId;
40
+	/**
41
+	 * Filters the results by user
42
+	 *
43
+	 * @param int $userId
44
+	 *
45
+	 * @return $this
46
+	 */
47
+	public function byUser($userId)
48
+	{
49
+		$this->whereClause .= ' AND user = ?';
50
+		$this->parameterList[] = $userId;
51 51
 
52
-        return $this;
53
-    }
52
+		return $this;
53
+	}
54 54
 
55
-    /**
56
-     * Filters the results by log action
57
-     *
58
-     * @param string $action
59
-     *
60
-     * @return $this
61
-     */
62
-    public function byAction($action)
63
-    {
64
-        $this->whereClause .= ' AND action = ?';
65
-        $this->parameterList[] = $action;
55
+	/**
56
+	 * Filters the results by log action
57
+	 *
58
+	 * @param string $action
59
+	 *
60
+	 * @return $this
61
+	 */
62
+	public function byAction($action)
63
+	{
64
+		$this->whereClause .= ' AND action = ?';
65
+		$this->parameterList[] = $action;
66 66
 
67
-        return $this;
68
-    }
67
+		return $this;
68
+	}
69 69
 
70
-    /**
71
-     * Filters the results by object type
72
-     *
73
-     * @param string $objectType
74
-     *
75
-     * @return $this
76
-     */
77
-    public function byObjectType($objectType)
78
-    {
79
-        $this->whereClause .= ' AND objecttype = ?';
80
-        $this->parameterList[] = $objectType;
70
+	/**
71
+	 * Filters the results by object type
72
+	 *
73
+	 * @param string $objectType
74
+	 *
75
+	 * @return $this
76
+	 */
77
+	public function byObjectType($objectType)
78
+	{
79
+		$this->whereClause .= ' AND objecttype = ?';
80
+		$this->parameterList[] = $objectType;
81 81
 
82
-        return $this;
83
-    }
82
+		return $this;
83
+	}
84 84
 
85
-    /**
86
-     * Filters the results by object type
87
-     *
88
-     * @param integer $objectId
89
-     *
90
-     * @return $this
91
-     */
92
-    public function byObjectId($objectId)
93
-    {
94
-        $this->whereClause .= ' AND objectid = ?';
95
-        $this->parameterList[] = $objectId;
85
+	/**
86
+	 * Filters the results by object type
87
+	 *
88
+	 * @param integer $objectId
89
+	 *
90
+	 * @return $this
91
+	 */
92
+	public function byObjectId($objectId)
93
+	{
94
+		$this->whereClause .= ' AND objectid = ?';
95
+		$this->parameterList[] = $objectId;
96 96
 
97
-        return $this;
98
-    }
97
+		return $this;
98
+	}
99 99
 }
100 100
\ No newline at end of file
Please login to merge, or discard this patch.
includes/Helpers/SearchHelpers/SearchHelperBase.php 1 patch
Indentation   +178 added lines, -178 removed lines patch added patch discarded remove patch
@@ -15,182 +15,182 @@
 block discarded – undo
15 15
 
16 16
 abstract class SearchHelperBase
17 17
 {
18
-    /** @var PdoDatabase */
19
-    protected $database;
20
-    /** @var array */
21
-    protected $parameterList = array();
22
-    /** @var null|int */
23
-    private $limit = null;
24
-    /** @var null|int */
25
-    private $offset = null;
26
-    private $orderBy = null;
27
-    /**
28
-     * @var string The where clause.
29
-     *
30
-     * (the 1=1 condition will be optimised out of the query by the query planner, and simplifies our code here). Note
31
-     * that we use positional parameters instead of named parameters because we don't know many times different options
32
-     * will be called (looking at excluding() here, but there's the option for others).
33
-     */
34
-    protected $whereClause = ' WHERE 1 = 1';
35
-    /** @var string */
36
-    protected $table;
37
-    protected $joinClause = '';
38
-    private $targetClass;
39
-
40
-    /**
41
-     * SearchHelperBase constructor.
42
-     *
43
-     * @param PdoDatabase $database
44
-     * @param string      $table
45
-     * @param             $targetClass
46
-     * @param null|string $order Order by clause, excluding ORDER BY.
47
-     */
48
-    protected function __construct(PdoDatabase $database, $table, $targetClass, $order = null)
49
-    {
50
-        $this->database = $database;
51
-        $this->table = $table;
52
-        $this->orderBy = $order;
53
-        $this->targetClass = $targetClass;
54
-    }
55
-
56
-    /**
57
-     * Finalises the database query, and executes it, returning a set of objects.
58
-     *
59
-     * @return DataObject[]
60
-     */
61
-    public function fetch()
62
-    {
63
-        $statement = $this->getData();
64
-
65
-        /** @var DataObject[] $returnedObjects */
66
-        $returnedObjects = $statement->fetchAll(PDO::FETCH_CLASS, $this->targetClass);
67
-        foreach ($returnedObjects as $req) {
68
-            $req->setDatabase($this->database);
69
-        }
70
-
71
-        return $returnedObjects;
72
-    }
73
-
74
-    /**
75
-     * Finalises the database query, and executes it, returning only the requested column.
76
-     *
77
-     * @param string $column The required column
78
-     * @return array
79
-     */
80
-    public function fetchColumn($column){
81
-        $statement = $this->getData($column);
82
-
83
-        return $statement->fetchAll(PDO::FETCH_COLUMN);
84
-    }
85
-
86
-    /**
87
-     * @param int $count Returns the record count of the result set
88
-     *
89
-     * @return $this
90
-     */
91
-    public function getRecordCount(&$count)
92
-    {
93
-        $query = 'SELECT /* SearchHelper */ COUNT(*) FROM ' . $this->table . ' origin ';
94
-        $query .= $this->joinClause . $this->whereClause;
95
-
96
-        $statement = $this->database->prepare($query);
97
-        $statement->execute($this->parameterList);
98
-
99
-        $count = $statement->fetchColumn(0);
100
-        $statement->closeCursor();
101
-
102
-        return $this;
103
-    }
104
-
105
-    /**
106
-     * Limits the results
107
-     *
108
-     * @param integer      $limit
109
-     * @param integer|null $offset
110
-     *
111
-     * @return $this
112
-     *
113
-     */
114
-    public function limit($limit, $offset = null)
115
-    {
116
-        $this->limit = $limit;
117
-        $this->offset = $offset;
118
-
119
-        return $this;
120
-    }
121
-
122
-    private function applyLimit()
123
-    {
124
-        $clause = '';
125
-        if ($this->limit !== null) {
126
-            $clause = ' LIMIT ?';
127
-            $this->parameterList[] = $this->limit;
128
-
129
-            if ($this->offset !== null) {
130
-                $clause .= ' OFFSET ?';
131
-                $this->parameterList[] = $this->offset;
132
-            }
133
-        }
134
-
135
-        return $clause;
136
-    }
137
-
138
-    private function applyOrder()
139
-    {
140
-        if ($this->orderBy !== null) {
141
-            return ' ORDER BY ' . $this->orderBy;
142
-        }
143
-
144
-        return '';
145
-    }
146
-
147
-    /**
148
-     * @param $column
149
-     *
150
-     * @return PDOStatement
151
-     */
152
-    private function getData($column = '*')
153
-    {
154
-        $query = $this->buildQuery($column);
155
-        $query .= $this->applyOrder();
156
-        $query .= $this->applyLimit();
157
-
158
-        $statement = $this->database->prepare($query);
159
-        $statement->execute($this->parameterList);
160
-
161
-        return $statement;
162
-    }
163
-
164
-    /**
165
-     * @param $column
166
-     *
167
-     * @return string
168
-     */
169
-    protected function buildQuery($column)
170
-    {
171
-        $query = 'SELECT /* SearchHelper */ origin.' . $column . ' FROM ' . $this->table . ' origin ';
172
-        $query .= $this->joinClause . $this->whereClause;
173
-
174
-        return $query;
175
-    }
176
-
177
-    public function inIds($idList) {
178
-        $this->inClause('id', $idList);
179
-        return $this;
180
-    }
181
-
182
-    protected function inClause($column, $values) {
183
-        if (count($values) === 0) {
184
-            return;
185
-        }
186
-
187
-        // Urgh. OK. You can't use IN() with parameters directly, so let's munge something together.
188
-        $valueCount = count($values);
189
-
190
-        // Firstly, let's create a string of question marks, which will do as positional parameters.
191
-        $inSection = str_repeat('?,', $valueCount - 1) . '?';
192
-
193
-        $this->whereClause .= " AND {$column} IN ({$inSection})";
194
-        $this->parameterList = array_merge($this->parameterList, $values);
195
-    }
18
+	/** @var PdoDatabase */
19
+	protected $database;
20
+	/** @var array */
21
+	protected $parameterList = array();
22
+	/** @var null|int */
23
+	private $limit = null;
24
+	/** @var null|int */
25
+	private $offset = null;
26
+	private $orderBy = null;
27
+	/**
28
+	 * @var string The where clause.
29
+	 *
30
+	 * (the 1=1 condition will be optimised out of the query by the query planner, and simplifies our code here). Note
31
+	 * that we use positional parameters instead of named parameters because we don't know many times different options
32
+	 * will be called (looking at excluding() here, but there's the option for others).
33
+	 */
34
+	protected $whereClause = ' WHERE 1 = 1';
35
+	/** @var string */
36
+	protected $table;
37
+	protected $joinClause = '';
38
+	private $targetClass;
39
+
40
+	/**
41
+	 * SearchHelperBase constructor.
42
+	 *
43
+	 * @param PdoDatabase $database
44
+	 * @param string      $table
45
+	 * @param             $targetClass
46
+	 * @param null|string $order Order by clause, excluding ORDER BY.
47
+	 */
48
+	protected function __construct(PdoDatabase $database, $table, $targetClass, $order = null)
49
+	{
50
+		$this->database = $database;
51
+		$this->table = $table;
52
+		$this->orderBy = $order;
53
+		$this->targetClass = $targetClass;
54
+	}
55
+
56
+	/**
57
+	 * Finalises the database query, and executes it, returning a set of objects.
58
+	 *
59
+	 * @return DataObject[]
60
+	 */
61
+	public function fetch()
62
+	{
63
+		$statement = $this->getData();
64
+
65
+		/** @var DataObject[] $returnedObjects */
66
+		$returnedObjects = $statement->fetchAll(PDO::FETCH_CLASS, $this->targetClass);
67
+		foreach ($returnedObjects as $req) {
68
+			$req->setDatabase($this->database);
69
+		}
70
+
71
+		return $returnedObjects;
72
+	}
73
+
74
+	/**
75
+	 * Finalises the database query, and executes it, returning only the requested column.
76
+	 *
77
+	 * @param string $column The required column
78
+	 * @return array
79
+	 */
80
+	public function fetchColumn($column){
81
+		$statement = $this->getData($column);
82
+
83
+		return $statement->fetchAll(PDO::FETCH_COLUMN);
84
+	}
85
+
86
+	/**
87
+	 * @param int $count Returns the record count of the result set
88
+	 *
89
+	 * @return $this
90
+	 */
91
+	public function getRecordCount(&$count)
92
+	{
93
+		$query = 'SELECT /* SearchHelper */ COUNT(*) FROM ' . $this->table . ' origin ';
94
+		$query .= $this->joinClause . $this->whereClause;
95
+
96
+		$statement = $this->database->prepare($query);
97
+		$statement->execute($this->parameterList);
98
+
99
+		$count = $statement->fetchColumn(0);
100
+		$statement->closeCursor();
101
+
102
+		return $this;
103
+	}
104
+
105
+	/**
106
+	 * Limits the results
107
+	 *
108
+	 * @param integer      $limit
109
+	 * @param integer|null $offset
110
+	 *
111
+	 * @return $this
112
+	 *
113
+	 */
114
+	public function limit($limit, $offset = null)
115
+	{
116
+		$this->limit = $limit;
117
+		$this->offset = $offset;
118
+
119
+		return $this;
120
+	}
121
+
122
+	private function applyLimit()
123
+	{
124
+		$clause = '';
125
+		if ($this->limit !== null) {
126
+			$clause = ' LIMIT ?';
127
+			$this->parameterList[] = $this->limit;
128
+
129
+			if ($this->offset !== null) {
130
+				$clause .= ' OFFSET ?';
131
+				$this->parameterList[] = $this->offset;
132
+			}
133
+		}
134
+
135
+		return $clause;
136
+	}
137
+
138
+	private function applyOrder()
139
+	{
140
+		if ($this->orderBy !== null) {
141
+			return ' ORDER BY ' . $this->orderBy;
142
+		}
143
+
144
+		return '';
145
+	}
146
+
147
+	/**
148
+	 * @param $column
149
+	 *
150
+	 * @return PDOStatement
151
+	 */
152
+	private function getData($column = '*')
153
+	{
154
+		$query = $this->buildQuery($column);
155
+		$query .= $this->applyOrder();
156
+		$query .= $this->applyLimit();
157
+
158
+		$statement = $this->database->prepare($query);
159
+		$statement->execute($this->parameterList);
160
+
161
+		return $statement;
162
+	}
163
+
164
+	/**
165
+	 * @param $column
166
+	 *
167
+	 * @return string
168
+	 */
169
+	protected function buildQuery($column)
170
+	{
171
+		$query = 'SELECT /* SearchHelper */ origin.' . $column . ' FROM ' . $this->table . ' origin ';
172
+		$query .= $this->joinClause . $this->whereClause;
173
+
174
+		return $query;
175
+	}
176
+
177
+	public function inIds($idList) {
178
+		$this->inClause('id', $idList);
179
+		return $this;
180
+	}
181
+
182
+	protected function inClause($column, $values) {
183
+		if (count($values) === 0) {
184
+			return;
185
+		}
186
+
187
+		// Urgh. OK. You can't use IN() with parameters directly, so let's munge something together.
188
+		$valueCount = count($values);
189
+
190
+		// Firstly, let's create a string of question marks, which will do as positional parameters.
191
+		$inSection = str_repeat('?,', $valueCount - 1) . '?';
192
+
193
+		$this->whereClause .= " AND {$column} IN ({$inSection})";
194
+		$this->parameterList = array_merge($this->parameterList, $values);
195
+	}
196 196
 }
Please login to merge, or discard this patch.
includes/Tasks/PageBase.php 1 patch
Indentation   +337 added lines, -337 removed lines patch added patch discarded remove patch
@@ -20,341 +20,341 @@
 block discarded – undo
20 20
 
21 21
 abstract class PageBase extends TaskBase implements IRoutedTask
22 22
 {
23
-    use TemplateOutput;
24
-    /** @var string Smarty template to display */
25
-    protected $template = "base.tpl";
26
-    /** @var string HTML title. Currently unused. */
27
-    protected $htmlTitle;
28
-    /** @var bool Determines if the page is a redirect or not */
29
-    protected $isRedirecting = false;
30
-    /** @var array Queue of headers to be sent on successful completion */
31
-    protected $headerQueue = array();
32
-    /** @var string The name of the route to use, as determined by the request router. */
33
-    private $routeName = null;
34
-    /** @var TokenManager */
35
-    protected $tokenManager;
36
-    /** @var string[] Extra CSS files to include */
37
-    private $extraCss = array();
38
-    /** @var string[] Extra JS files to include */
39
-    private $extraJs = array();
40
-
41
-    /**
42
-     * Sets the route the request will take. Only should be called from the request router or barrier test.
43
-     *
44
-     * @param string $routeName        The name of the route
45
-     * @param bool   $skipCallableTest Don't use this unless you know what you're doing, and what the implications are.
46
-     *
47
-     * @throws Exception
48
-     * @category Security-Critical
49
-     */
50
-    final public function setRoute($routeName, $skipCallableTest = false)
51
-    {
52
-        // Test the new route is callable before adopting it.
53
-        if (!$skipCallableTest && !is_callable(array($this, $routeName))) {
54
-            throw new Exception("Proposed route '$routeName' is not callable.");
55
-        }
56
-
57
-        // Adopt the new route
58
-        $this->routeName = $routeName;
59
-    }
60
-
61
-    /**
62
-     * Gets the name of the route that has been passed from the request router.
63
-     * @return string
64
-     */
65
-    final public function getRouteName()
66
-    {
67
-        return $this->routeName;
68
-    }
69
-
70
-    /**
71
-     * Performs generic page setup actions
72
-     */
73
-    final protected function setupPage()
74
-    {
75
-        $this->setUpSmarty();
76
-
77
-        $siteNoticeText = SiteNotice::get($this->getDatabase());
78
-
79
-        $this->assign('siteNoticeText', $siteNoticeText);
80
-
81
-        $currentUser = User::getCurrent($this->getDatabase());
82
-        $this->assign('currentUser', $currentUser);
83
-        $this->assign('loggedIn', (!$currentUser->isCommunityUser()));
84
-    }
85
-
86
-    /**
87
-     * Runs the page logic as routed by the RequestRouter
88
-     *
89
-     * Only should be called after a security barrier! That means only from execute().
90
-     */
91
-    final protected function runPage()
92
-    {
93
-        $database = $this->getDatabase();
94
-
95
-        // initialise a database transaction
96
-        if (!$database->beginTransaction()) {
97
-            throw new Exception('Failed to start transaction on primary database.');
98
-        }
99
-
100
-        try {
101
-            // run the page code
102
-            $this->{$this->getRouteName()}();
103
-
104
-            $database->commit();
105
-        }
106
-        catch (ApplicationLogicException $ex) {
107
-            // it's an application logic exception, so nothing went seriously wrong with the site. We can use the
108
-            // standard templating system for this.
109
-
110
-            // Firstly, let's undo anything that happened to the database.
111
-            $database->rollBack();
112
-
113
-            // Reset smarty
114
-            $this->setUpSmarty();
115
-
116
-            // Set the template
117
-            $this->setTemplate('exception/application-logic.tpl');
118
-            $this->assign('message', $ex->getMessage());
119
-
120
-            // Force this back to false
121
-            $this->isRedirecting = false;
122
-            $this->headerQueue = array();
123
-        }
124
-        catch (OptimisticLockFailedException $ex) {
125
-            // it's an optimistic lock failure exception, so nothing went seriously wrong with the site. We can use the
126
-            // standard templating system for this.
127
-
128
-            // Firstly, let's undo anything that happened to the database.
129
-            $database->rollBack();
130
-
131
-            // Reset smarty
132
-            $this->setUpSmarty();
133
-
134
-            // Set the template
135
-            $this->setTemplate('exception/optimistic-lock-failure.tpl');
136
-            $this->assign('message', $ex->getMessage());
137
-
138
-            // Force this back to false
139
-            $this->isRedirecting = false;
140
-            $this->headerQueue = array();
141
-        }
142
-        finally {
143
-            // Catch any hanging on transactions
144
-            if ($database->hasActiveTransaction()) {
145
-                $database->rollBack();
146
-            }
147
-        }
148
-
149
-        // run any finalisation code needed before we send the output to the browser.
150
-        $this->finalisePage();
151
-
152
-        // Send the headers
153
-        $this->sendResponseHeaders();
154
-
155
-        // Check we have a template to use!
156
-        if ($this->template !== null) {
157
-            $content = $this->fetchTemplate($this->template);
158
-            ob_clean();
159
-            print($content);
160
-            ob_flush();
161
-
162
-            return;
163
-        }
164
-    }
165
-
166
-    /**
167
-     * Performs final tasks needed before rendering the page.
168
-     */
169
-    protected function finalisePage()
170
-    {
171
-        if ($this->isRedirecting) {
172
-            $this->template = null;
173
-
174
-            return;
175
-        }
176
-
177
-        $this->assign('extraCss', $this->extraCss);
178
-        $this->assign('extraJs', $this->extraJs);
179
-
180
-        // If we're actually displaying content, we want to add the session alerts here!
181
-        $this->assign('alerts', SessionAlert::getAlerts());
182
-        SessionAlert::clearAlerts();
183
-
184
-        $this->assign('htmlTitle', $this->htmlTitle);
185
-    }
186
-
187
-    /**
188
-     * @return TokenManager
189
-     */
190
-    public function getTokenManager()
191
-    {
192
-        return $this->tokenManager;
193
-    }
194
-
195
-    /**
196
-     * @param TokenManager $tokenManager
197
-     */
198
-    public function setTokenManager($tokenManager)
199
-    {
200
-        $this->tokenManager = $tokenManager;
201
-    }
202
-
203
-    /**
204
-     * Sends the redirect headers to perform a GET at the destination page.
205
-     *
206
-     * Also nullifies the set template so Smarty does not render it.
207
-     *
208
-     * @param string      $page   The page to redirect requests to (as used in the UR)
209
-     * @param null|string $action The action to use on the page.
210
-     * @param null|array  $parameters
211
-     * @param null|string $script The script (relative to index.php) to redirect to
212
-     */
213
-    final protected function redirect($page = '', $action = null, $parameters = null, $script = null)
214
-    {
215
-        $currentScriptName = WebRequest::scriptName();
216
-
217
-        // Are we changing script?
218
-        if ($script === null || substr($currentScriptName, -1 * count($script)) === $script) {
219
-            $targetScriptName = $currentScriptName;
220
-        }
221
-        else {
222
-            $targetScriptName = $this->getSiteConfiguration()->getBaseUrl() . '/' . $script;
223
-        }
224
-
225
-        $pathInfo = array($targetScriptName);
226
-
227
-        $pathInfo[1] = $page;
228
-
229
-        if ($action !== null) {
230
-            $pathInfo[2] = $action;
231
-        }
232
-
233
-        $url = implode('/', $pathInfo);
234
-
235
-        if (is_array($parameters) && count($parameters) > 0) {
236
-            $url .= '?' . http_build_query($parameters);
237
-        }
238
-
239
-        $this->redirectUrl($url);
240
-    }
241
-
242
-    /**
243
-     * Sends the redirect headers to perform a GET at the new address.
244
-     *
245
-     * Also nullifies the set template so Smarty does not render it.
246
-     *
247
-     * @param string $path URL to redirect to
248
-     */
249
-    final protected function redirectUrl($path)
250
-    {
251
-        // 303 See Other = re-request at new address with a GET.
252
-        $this->headerQueue[] = 'HTTP/1.1 303 See Other';
253
-        $this->headerQueue[] = "Location: $path";
254
-
255
-        $this->setTemplate(null);
256
-        $this->isRedirecting = true;
257
-    }
258
-
259
-    /**
260
-     * Sets the name of the template this page should display.
261
-     *
262
-     * @param string $name
263
-     *
264
-     * @throws Exception
265
-     */
266
-    final protected function setTemplate($name)
267
-    {
268
-        if ($this->isRedirecting) {
269
-            throw new Exception('This page has been set as a redirect, no template can be displayed!');
270
-        }
271
-
272
-        $this->template = $name;
273
-    }
274
-
275
-    /**
276
-     * Adds an extra CSS file to to the page
277
-     *
278
-     * @param string $path The path (relative to the application root) of the file
279
-     */
280
-    final protected function addCss($path) {
281
-        if(in_array($path, $this->extraCss)){
282
-            // nothing to do
283
-            return;
284
-        }
285
-
286
-        $this->extraCss[] = $path;
287
-    }
288
-
289
-    /**
290
-     * Adds an extra JS file to to the page
291
-     *
292
-     * @param string $path The path (relative to the application root) of the file
293
-     */
294
-    final protected function addJs($path){
295
-        if(in_array($path, $this->extraJs)){
296
-            // nothing to do
297
-            return;
298
-        }
299
-
300
-        $this->extraJs[] = $path;
301
-    }
302
-
303
-    /**
304
-     * Main function for this page, when no specific actions are called.
305
-     * @return void
306
-     */
307
-    abstract protected function main();
308
-
309
-    /**
310
-     * @param string $title
311
-     */
312
-    final protected function setHtmlTitle($title)
313
-    {
314
-        $this->htmlTitle = $title;
315
-    }
316
-
317
-    public function execute()
318
-    {
319
-        if ($this->getRouteName() === null) {
320
-            throw new Exception('Request is unrouted.');
321
-        }
322
-
323
-        if ($this->getSiteConfiguration() === null) {
324
-            throw new Exception('Page has no configuration!');
325
-        }
326
-
327
-        $this->setupPage();
328
-
329
-        $this->runPage();
330
-    }
331
-
332
-    public function assignCSRFToken()
333
-    {
334
-        $token = $this->tokenManager->getNewToken();
335
-        $this->assign('csrfTokenData', $token->getTokenData());
336
-    }
337
-
338
-    public function validateCSRFToken()
339
-    {
340
-        if (!$this->tokenManager->validateToken(WebRequest::postString('csrfTokenData'))) {
341
-            throw new ApplicationLogicException('Form token is not valid, please reload and try again');
342
-        }
343
-    }
344
-
345
-    protected function sendResponseHeaders()
346
-    {
347
-        if (headers_sent()) {
348
-            throw new ApplicationLogicException          ('Headers have already been sent! This is likely a bug in the application.');
349
-        }
350
-
351
-        foreach ($this->headerQueue as $item) {
352
-            if (mb_strpos($item, "\r") !== false || mb_strpos($item, "\n") !== false) {
353
-                // Oops. We're not allowed to do this.
354
-                throw new Exception('Unable to split header');
355
-            }
356
-
357
-            header($item);
358
-        }
359
-    }
23
+	use TemplateOutput;
24
+	/** @var string Smarty template to display */
25
+	protected $template = "base.tpl";
26
+	/** @var string HTML title. Currently unused. */
27
+	protected $htmlTitle;
28
+	/** @var bool Determines if the page is a redirect or not */
29
+	protected $isRedirecting = false;
30
+	/** @var array Queue of headers to be sent on successful completion */
31
+	protected $headerQueue = array();
32
+	/** @var string The name of the route to use, as determined by the request router. */
33
+	private $routeName = null;
34
+	/** @var TokenManager */
35
+	protected $tokenManager;
36
+	/** @var string[] Extra CSS files to include */
37
+	private $extraCss = array();
38
+	/** @var string[] Extra JS files to include */
39
+	private $extraJs = array();
40
+
41
+	/**
42
+	 * Sets the route the request will take. Only should be called from the request router or barrier test.
43
+	 *
44
+	 * @param string $routeName        The name of the route
45
+	 * @param bool   $skipCallableTest Don't use this unless you know what you're doing, and what the implications are.
46
+	 *
47
+	 * @throws Exception
48
+	 * @category Security-Critical
49
+	 */
50
+	final public function setRoute($routeName, $skipCallableTest = false)
51
+	{
52
+		// Test the new route is callable before adopting it.
53
+		if (!$skipCallableTest && !is_callable(array($this, $routeName))) {
54
+			throw new Exception("Proposed route '$routeName' is not callable.");
55
+		}
56
+
57
+		// Adopt the new route
58
+		$this->routeName = $routeName;
59
+	}
60
+
61
+	/**
62
+	 * Gets the name of the route that has been passed from the request router.
63
+	 * @return string
64
+	 */
65
+	final public function getRouteName()
66
+	{
67
+		return $this->routeName;
68
+	}
69
+
70
+	/**
71
+	 * Performs generic page setup actions
72
+	 */
73
+	final protected function setupPage()
74
+	{
75
+		$this->setUpSmarty();
76
+
77
+		$siteNoticeText = SiteNotice::get($this->getDatabase());
78
+
79
+		$this->assign('siteNoticeText', $siteNoticeText);
80
+
81
+		$currentUser = User::getCurrent($this->getDatabase());
82
+		$this->assign('currentUser', $currentUser);
83
+		$this->assign('loggedIn', (!$currentUser->isCommunityUser()));
84
+	}
85
+
86
+	/**
87
+	 * Runs the page logic as routed by the RequestRouter
88
+	 *
89
+	 * Only should be called after a security barrier! That means only from execute().
90
+	 */
91
+	final protected function runPage()
92
+	{
93
+		$database = $this->getDatabase();
94
+
95
+		// initialise a database transaction
96
+		if (!$database->beginTransaction()) {
97
+			throw new Exception('Failed to start transaction on primary database.');
98
+		}
99
+
100
+		try {
101
+			// run the page code
102
+			$this->{$this->getRouteName()}();
103
+
104
+			$database->commit();
105
+		}
106
+		catch (ApplicationLogicException $ex) {
107
+			// it's an application logic exception, so nothing went seriously wrong with the site. We can use the
108
+			// standard templating system for this.
109
+
110
+			// Firstly, let's undo anything that happened to the database.
111
+			$database->rollBack();
112
+
113
+			// Reset smarty
114
+			$this->setUpSmarty();
115
+
116
+			// Set the template
117
+			$this->setTemplate('exception/application-logic.tpl');
118
+			$this->assign('message', $ex->getMessage());
119
+
120
+			// Force this back to false
121
+			$this->isRedirecting = false;
122
+			$this->headerQueue = array();
123
+		}
124
+		catch (OptimisticLockFailedException $ex) {
125
+			// it's an optimistic lock failure exception, so nothing went seriously wrong with the site. We can use the
126
+			// standard templating system for this.
127
+
128
+			// Firstly, let's undo anything that happened to the database.
129
+			$database->rollBack();
130
+
131
+			// Reset smarty
132
+			$this->setUpSmarty();
133
+
134
+			// Set the template
135
+			$this->setTemplate('exception/optimistic-lock-failure.tpl');
136
+			$this->assign('message', $ex->getMessage());
137
+
138
+			// Force this back to false
139
+			$this->isRedirecting = false;
140
+			$this->headerQueue = array();
141
+		}
142
+		finally {
143
+			// Catch any hanging on transactions
144
+			if ($database->hasActiveTransaction()) {
145
+				$database->rollBack();
146
+			}
147
+		}
148
+
149
+		// run any finalisation code needed before we send the output to the browser.
150
+		$this->finalisePage();
151
+
152
+		// Send the headers
153
+		$this->sendResponseHeaders();
154
+
155
+		// Check we have a template to use!
156
+		if ($this->template !== null) {
157
+			$content = $this->fetchTemplate($this->template);
158
+			ob_clean();
159
+			print($content);
160
+			ob_flush();
161
+
162
+			return;
163
+		}
164
+	}
165
+
166
+	/**
167
+	 * Performs final tasks needed before rendering the page.
168
+	 */
169
+	protected function finalisePage()
170
+	{
171
+		if ($this->isRedirecting) {
172
+			$this->template = null;
173
+
174
+			return;
175
+		}
176
+
177
+		$this->assign('extraCss', $this->extraCss);
178
+		$this->assign('extraJs', $this->extraJs);
179
+
180
+		// If we're actually displaying content, we want to add the session alerts here!
181
+		$this->assign('alerts', SessionAlert::getAlerts());
182
+		SessionAlert::clearAlerts();
183
+
184
+		$this->assign('htmlTitle', $this->htmlTitle);
185
+	}
186
+
187
+	/**
188
+	 * @return TokenManager
189
+	 */
190
+	public function getTokenManager()
191
+	{
192
+		return $this->tokenManager;
193
+	}
194
+
195
+	/**
196
+	 * @param TokenManager $tokenManager
197
+	 */
198
+	public function setTokenManager($tokenManager)
199
+	{
200
+		$this->tokenManager = $tokenManager;
201
+	}
202
+
203
+	/**
204
+	 * Sends the redirect headers to perform a GET at the destination page.
205
+	 *
206
+	 * Also nullifies the set template so Smarty does not render it.
207
+	 *
208
+	 * @param string      $page   The page to redirect requests to (as used in the UR)
209
+	 * @param null|string $action The action to use on the page.
210
+	 * @param null|array  $parameters
211
+	 * @param null|string $script The script (relative to index.php) to redirect to
212
+	 */
213
+	final protected function redirect($page = '', $action = null, $parameters = null, $script = null)
214
+	{
215
+		$currentScriptName = WebRequest::scriptName();
216
+
217
+		// Are we changing script?
218
+		if ($script === null || substr($currentScriptName, -1 * count($script)) === $script) {
219
+			$targetScriptName = $currentScriptName;
220
+		}
221
+		else {
222
+			$targetScriptName = $this->getSiteConfiguration()->getBaseUrl() . '/' . $script;
223
+		}
224
+
225
+		$pathInfo = array($targetScriptName);
226
+
227
+		$pathInfo[1] = $page;
228
+
229
+		if ($action !== null) {
230
+			$pathInfo[2] = $action;
231
+		}
232
+
233
+		$url = implode('/', $pathInfo);
234
+
235
+		if (is_array($parameters) && count($parameters) > 0) {
236
+			$url .= '?' . http_build_query($parameters);
237
+		}
238
+
239
+		$this->redirectUrl($url);
240
+	}
241
+
242
+	/**
243
+	 * Sends the redirect headers to perform a GET at the new address.
244
+	 *
245
+	 * Also nullifies the set template so Smarty does not render it.
246
+	 *
247
+	 * @param string $path URL to redirect to
248
+	 */
249
+	final protected function redirectUrl($path)
250
+	{
251
+		// 303 See Other = re-request at new address with a GET.
252
+		$this->headerQueue[] = 'HTTP/1.1 303 See Other';
253
+		$this->headerQueue[] = "Location: $path";
254
+
255
+		$this->setTemplate(null);
256
+		$this->isRedirecting = true;
257
+	}
258
+
259
+	/**
260
+	 * Sets the name of the template this page should display.
261
+	 *
262
+	 * @param string $name
263
+	 *
264
+	 * @throws Exception
265
+	 */
266
+	final protected function setTemplate($name)
267
+	{
268
+		if ($this->isRedirecting) {
269
+			throw new Exception('This page has been set as a redirect, no template can be displayed!');
270
+		}
271
+
272
+		$this->template = $name;
273
+	}
274
+
275
+	/**
276
+	 * Adds an extra CSS file to to the page
277
+	 *
278
+	 * @param string $path The path (relative to the application root) of the file
279
+	 */
280
+	final protected function addCss($path) {
281
+		if(in_array($path, $this->extraCss)){
282
+			// nothing to do
283
+			return;
284
+		}
285
+
286
+		$this->extraCss[] = $path;
287
+	}
288
+
289
+	/**
290
+	 * Adds an extra JS file to to the page
291
+	 *
292
+	 * @param string $path The path (relative to the application root) of the file
293
+	 */
294
+	final protected function addJs($path){
295
+		if(in_array($path, $this->extraJs)){
296
+			// nothing to do
297
+			return;
298
+		}
299
+
300
+		$this->extraJs[] = $path;
301
+	}
302
+
303
+	/**
304
+	 * Main function for this page, when no specific actions are called.
305
+	 * @return void
306
+	 */
307
+	abstract protected function main();
308
+
309
+	/**
310
+	 * @param string $title
311
+	 */
312
+	final protected function setHtmlTitle($title)
313
+	{
314
+		$this->htmlTitle = $title;
315
+	}
316
+
317
+	public function execute()
318
+	{
319
+		if ($this->getRouteName() === null) {
320
+			throw new Exception('Request is unrouted.');
321
+		}
322
+
323
+		if ($this->getSiteConfiguration() === null) {
324
+			throw new Exception('Page has no configuration!');
325
+		}
326
+
327
+		$this->setupPage();
328
+
329
+		$this->runPage();
330
+	}
331
+
332
+	public function assignCSRFToken()
333
+	{
334
+		$token = $this->tokenManager->getNewToken();
335
+		$this->assign('csrfTokenData', $token->getTokenData());
336
+	}
337
+
338
+	public function validateCSRFToken()
339
+	{
340
+		if (!$this->tokenManager->validateToken(WebRequest::postString('csrfTokenData'))) {
341
+			throw new ApplicationLogicException('Form token is not valid, please reload and try again');
342
+		}
343
+	}
344
+
345
+	protected function sendResponseHeaders()
346
+	{
347
+		if (headers_sent()) {
348
+			throw new ApplicationLogicException          ('Headers have already been sent! This is likely a bug in the application.');
349
+		}
350
+
351
+		foreach ($this->headerQueue as $item) {
352
+			if (mb_strpos($item, "\r") !== false || mb_strpos($item, "\n") !== false) {
353
+				// Oops. We're not allowed to do this.
354
+				throw new Exception('Unable to split header');
355
+			}
356
+
357
+			header($item);
358
+		}
359
+	}
360 360
 }
Please login to merge, or discard this patch.
includes/DataObjects/User.php 1 patch
Indentation   +1128 added lines, -1128 removed lines patch added patch discarded remove patch
@@ -27,285 +27,285 @@  discard block
 block discarded – undo
27 27
  */
28 28
 class User extends DataObject
29 29
 {
30
-    const STATUS_USER = 'User';
31
-    const STATUS_ADMIN = 'Admin';
32
-    const STATUS_SUSPENDED = 'Suspended';
33
-    const STATUS_DECLINED = 'Declined';
34
-    const STATUS_NEW = 'New';
35
-    private $username;
36
-    private $email;
37
-    private $password;
38
-    private $status = self::STATUS_NEW;
39
-    private $onwikiname = "##OAUTH##";
40
-    private $welcome_sig = "";
41
-    private $lastactive = "0000-00-00 00:00:00";
42
-    private $forcelogout = 0;
43
-    private $checkuser = 0;
44
-    private $forceidentified = null;
45
-    private $welcome_template = 0;
46
-    private $abortpref = 0;
47
-    private $confirmationdiff = 0;
48
-    private $emailsig = "";
49
-    /** @var null|string */
50
-    private $oauthrequesttoken = null;
51
-    /** @var null|string */
52
-    private $oauthrequestsecret = null;
53
-    /** @var null|string */
54
-    private $oauthaccesstoken = null;
55
-    /** @var null|string */
56
-    private $oauthaccesssecret = null;
57
-    private $oauthidentitycache = null;
58
-    /** @var User Cache variable of the current user - it's never going to change in the middle of a request. */
59
-    private static $currentUser;
60
-    /** @var null|JWT The identity cache */
61
-    private $identityCache = null;
62
-    #region Object load methods
63
-
64
-    /**
65
-     * Gets the currently logged in user
66
-     *
67
-     * @param PdoDatabase $database
68
-     *
69
-     * @return User|CommunityUser
70
-     */
71
-    public static function getCurrent(PdoDatabase $database)
72
-    {
73
-        if (self::$currentUser === null) {
74
-            $sessionId = WebRequest::getSessionUserId();
75
-
76
-            if ($sessionId !== null) {
77
-                /** @var User $user */
78
-                $user = self::getById($sessionId, $database);
79
-
80
-                if ($user === false) {
81
-                    self::$currentUser = new CommunityUser();
82
-                }
83
-                else {
84
-                    self::$currentUser = $user;
85
-                }
86
-            }
87
-            else {
88
-                $anonymousCoward = new CommunityUser();
89
-
90
-                self::$currentUser = $anonymousCoward;
91
-            }
92
-        }
93
-
94
-        return self::$currentUser;
95
-    }
96
-
97
-    /**
98
-     * Gets a user by their user ID
99
-     *
100
-     * Pass -1 to get the community user.
101
-     *
102
-     * @param int|null    $id
103
-     * @param PdoDatabase $database
104
-     *
105
-     * @return User|false
106
-     */
107
-    public static function getById($id, PdoDatabase $database)
108
-    {
109
-        if ($id === null || $id == -1) {
110
-            return new CommunityUser();
111
-        }
112
-
113
-        /** @var User|false $user */
114
-        $user = parent::getById($id, $database);
115
-
116
-        return $user;
117
-    }
118
-
119
-    /**
120
-     * @return CommunityUser
121
-     */
122
-    public static function getCommunity()
123
-    {
124
-        return new CommunityUser();
125
-    }
126
-
127
-    /**
128
-     * Gets a user by their username
129
-     *
130
-     * @param  string      $username
131
-     * @param  PdoDatabase $database
132
-     *
133
-     * @return CommunityUser|User|false
134
-     */
135
-    public static function getByUsername($username, PdoDatabase $database)
136
-    {
137
-        global $communityUsername;
138
-        if ($username == $communityUsername) {
139
-            return new CommunityUser();
140
-        }
141
-
142
-        $statement = $database->prepare("SELECT * FROM user WHERE username = :id LIMIT 1;");
143
-        $statement->bindValue(":id", $username);
144
-
145
-        $statement->execute();
146
-
147
-        $resultObject = $statement->fetchObject(get_called_class());
148
-
149
-        if ($resultObject != false) {
150
-            $resultObject->setDatabase($database);
151
-        }
152
-
153
-        return $resultObject;
154
-    }
155
-
156
-    /**
157
-     * Gets a user by their on-wiki username.
158
-     *
159
-     * Don't use without asking me first. It's really inefficient in it's current implementation.
160
-     * We need to restructure the user table again to make this more efficient.
161
-     * We don't actually store the on-wiki name in the table any more, instead we
162
-     * are storing JSON in a column (!!). Yep, my fault. Code review is an awesome thing.
163
-     *            -- stw 2015-10-20
164
-     *
165
-     * @param string      $username
166
-     * @param PdoDatabase $database
167
-     *
168
-     * @return User|false
169
-     */
170
-    public static function getByOnWikiUsername($username, PdoDatabase $database)
171
-    {
172
-        // Firstly, try to search by the efficient database lookup.
173
-        $statement = $database->prepare("SELECT * FROM user WHERE onwikiname = :id LIMIT 1;");
174
-        $statement->bindValue(":id", $username);
175
-        $statement->execute();
176
-
177
-        $resultObject = $statement->fetchObject(get_called_class());
178
-
179
-        if ($resultObject != false) {
180
-            $resultObject->setDatabase($database);
181
-
182
-            return $resultObject;
183
-        }
184
-
185
-        // For active users, the above has failed. Let's do it the hard way.
186
-        $sqlStatement = "SELECT * FROM user WHERE onwikiname = '##OAUTH##' AND oauthaccesstoken IS NOT NULL;";
187
-        $statement = $database->prepare($sqlStatement);
188
-        $statement->execute();
189
-        $resultSet = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
190
-
191
-        /** @var User $user */
192
-        foreach ($resultSet as $user) {
193
-            // We have to set this before doing OAuth queries. :(
194
-            $user->setDatabase($database);
195
-
196
-            // Using cached data here!
197
-            if ($user->getOAuthOnWikiName(true) == $username) {
198
-                // Success.
199
-                return $user;
200
-            }
201
-        }
202
-
203
-        // Cached data failed. Let's do it the *REALLY* hard way.
204
-        foreach ($resultSet as $user) {
205
-            // We have to set this before doing OAuth queries. :(
206
-            $user->setDatabase($database);
207
-
208
-            // Don't use the cached data, but instead query the API.
209
-            if ($user->getOAuthOnWikiName(false) == $username) {
210
-                // Success.
211
-                return $user;
212
-            }
213
-        }
214
-
215
-        // Nope. Sorry.
216
-        return false;
217
-    }
218
-
219
-    /**
220
-     * Gets a user by their OAuth request token
221
-     *
222
-     * @param string      $requestToken
223
-     * @param PdoDatabase $database
224
-     *
225
-     * @return User|false
226
-     */
227
-    public static function getByRequestToken($requestToken, PdoDatabase $database)
228
-    {
229
-        $statement = $database->prepare("SELECT * FROM user WHERE oauthrequesttoken = :id LIMIT 1;");
230
-        $statement->bindValue(":id", $requestToken);
231
-
232
-        $statement->execute();
233
-
234
-        $resultObject = $statement->fetchObject(get_called_class());
235
-
236
-        if ($resultObject != false) {
237
-            $resultObject->setDatabase($database);
238
-        }
239
-
240
-        return $resultObject;
241
-    }
242
-
243
-    /**
244
-     * Gets all users with a supplied status
245
-     *
246
-     * @param string      $status
247
-     * @param PdoDatabase $database
248
-     *
249
-     * @return User[]
250
-     */
251
-    public static function getAllWithStatus($status, PdoDatabase $database)
252
-    {
253
-        $statement = $database->prepare("SELECT * FROM user WHERE status = :status");
254
-        $statement->execute(array(":status" => $status));
255
-
256
-        $resultObject = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
257
-
258
-        /** @var User $u */
259
-        foreach ($resultObject as $u) {
260
-            $u->setDatabase($database);
261
-        }
262
-
263
-        return $resultObject;
264
-    }
265
-
266
-    /**
267
-     * Gets all checkusers
268
-     *
269
-     * @param PdoDatabase $database
270
-     *
271
-     * @return User[]
272
-     */
273
-    public static function getAllCheckusers(PdoDatabase $database)
274
-    {
275
-        $statement = $database->prepare("SELECT * FROM user WHERE checkuser = 1;");
276
-        $statement->execute();
277
-
278
-        $resultObject = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
279
-
280
-        $resultsCollection = array();
281
-
282
-        /** @var User $u */
283
-        foreach ($resultObject as $u) {
284
-            $u->setDatabase($database);
285
-
286
-            if (!$u->isCheckuser()) {
287
-                continue;
288
-            }
289
-
290
-            $resultsCollection[] = $u;
291
-        }
292
-
293
-        return $resultsCollection;
294
-    }
295
-
296
-    /**
297
-     * Gets all inactive users
298
-     *
299
-     * @param PdoDatabase $database
300
-     *
301
-     * @return User[]
302
-     */
303
-    public static function getAllInactive(PdoDatabase $database)
304
-    {
305
-        $date = new DateTime();
306
-        $date->modify("-45 days");
307
-
308
-        $statement = $database->prepare(<<<SQL
30
+	const STATUS_USER = 'User';
31
+	const STATUS_ADMIN = 'Admin';
32
+	const STATUS_SUSPENDED = 'Suspended';
33
+	const STATUS_DECLINED = 'Declined';
34
+	const STATUS_NEW = 'New';
35
+	private $username;
36
+	private $email;
37
+	private $password;
38
+	private $status = self::STATUS_NEW;
39
+	private $onwikiname = "##OAUTH##";
40
+	private $welcome_sig = "";
41
+	private $lastactive = "0000-00-00 00:00:00";
42
+	private $forcelogout = 0;
43
+	private $checkuser = 0;
44
+	private $forceidentified = null;
45
+	private $welcome_template = 0;
46
+	private $abortpref = 0;
47
+	private $confirmationdiff = 0;
48
+	private $emailsig = "";
49
+	/** @var null|string */
50
+	private $oauthrequesttoken = null;
51
+	/** @var null|string */
52
+	private $oauthrequestsecret = null;
53
+	/** @var null|string */
54
+	private $oauthaccesstoken = null;
55
+	/** @var null|string */
56
+	private $oauthaccesssecret = null;
57
+	private $oauthidentitycache = null;
58
+	/** @var User Cache variable of the current user - it's never going to change in the middle of a request. */
59
+	private static $currentUser;
60
+	/** @var null|JWT The identity cache */
61
+	private $identityCache = null;
62
+	#region Object load methods
63
+
64
+	/**
65
+	 * Gets the currently logged in user
66
+	 *
67
+	 * @param PdoDatabase $database
68
+	 *
69
+	 * @return User|CommunityUser
70
+	 */
71
+	public static function getCurrent(PdoDatabase $database)
72
+	{
73
+		if (self::$currentUser === null) {
74
+			$sessionId = WebRequest::getSessionUserId();
75
+
76
+			if ($sessionId !== null) {
77
+				/** @var User $user */
78
+				$user = self::getById($sessionId, $database);
79
+
80
+				if ($user === false) {
81
+					self::$currentUser = new CommunityUser();
82
+				}
83
+				else {
84
+					self::$currentUser = $user;
85
+				}
86
+			}
87
+			else {
88
+				$anonymousCoward = new CommunityUser();
89
+
90
+				self::$currentUser = $anonymousCoward;
91
+			}
92
+		}
93
+
94
+		return self::$currentUser;
95
+	}
96
+
97
+	/**
98
+	 * Gets a user by their user ID
99
+	 *
100
+	 * Pass -1 to get the community user.
101
+	 *
102
+	 * @param int|null    $id
103
+	 * @param PdoDatabase $database
104
+	 *
105
+	 * @return User|false
106
+	 */
107
+	public static function getById($id, PdoDatabase $database)
108
+	{
109
+		if ($id === null || $id == -1) {
110
+			return new CommunityUser();
111
+		}
112
+
113
+		/** @var User|false $user */
114
+		$user = parent::getById($id, $database);
115
+
116
+		return $user;
117
+	}
118
+
119
+	/**
120
+	 * @return CommunityUser
121
+	 */
122
+	public static function getCommunity()
123
+	{
124
+		return new CommunityUser();
125
+	}
126
+
127
+	/**
128
+	 * Gets a user by their username
129
+	 *
130
+	 * @param  string      $username
131
+	 * @param  PdoDatabase $database
132
+	 *
133
+	 * @return CommunityUser|User|false
134
+	 */
135
+	public static function getByUsername($username, PdoDatabase $database)
136
+	{
137
+		global $communityUsername;
138
+		if ($username == $communityUsername) {
139
+			return new CommunityUser();
140
+		}
141
+
142
+		$statement = $database->prepare("SELECT * FROM user WHERE username = :id LIMIT 1;");
143
+		$statement->bindValue(":id", $username);
144
+
145
+		$statement->execute();
146
+
147
+		$resultObject = $statement->fetchObject(get_called_class());
148
+
149
+		if ($resultObject != false) {
150
+			$resultObject->setDatabase($database);
151
+		}
152
+
153
+		return $resultObject;
154
+	}
155
+
156
+	/**
157
+	 * Gets a user by their on-wiki username.
158
+	 *
159
+	 * Don't use without asking me first. It's really inefficient in it's current implementation.
160
+	 * We need to restructure the user table again to make this more efficient.
161
+	 * We don't actually store the on-wiki name in the table any more, instead we
162
+	 * are storing JSON in a column (!!). Yep, my fault. Code review is an awesome thing.
163
+	 *            -- stw 2015-10-20
164
+	 *
165
+	 * @param string      $username
166
+	 * @param PdoDatabase $database
167
+	 *
168
+	 * @return User|false
169
+	 */
170
+	public static function getByOnWikiUsername($username, PdoDatabase $database)
171
+	{
172
+		// Firstly, try to search by the efficient database lookup.
173
+		$statement = $database->prepare("SELECT * FROM user WHERE onwikiname = :id LIMIT 1;");
174
+		$statement->bindValue(":id", $username);
175
+		$statement->execute();
176
+
177
+		$resultObject = $statement->fetchObject(get_called_class());
178
+
179
+		if ($resultObject != false) {
180
+			$resultObject->setDatabase($database);
181
+
182
+			return $resultObject;
183
+		}
184
+
185
+		// For active users, the above has failed. Let's do it the hard way.
186
+		$sqlStatement = "SELECT * FROM user WHERE onwikiname = '##OAUTH##' AND oauthaccesstoken IS NOT NULL;";
187
+		$statement = $database->prepare($sqlStatement);
188
+		$statement->execute();
189
+		$resultSet = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
190
+
191
+		/** @var User $user */
192
+		foreach ($resultSet as $user) {
193
+			// We have to set this before doing OAuth queries. :(
194
+			$user->setDatabase($database);
195
+
196
+			// Using cached data here!
197
+			if ($user->getOAuthOnWikiName(true) == $username) {
198
+				// Success.
199
+				return $user;
200
+			}
201
+		}
202
+
203
+		// Cached data failed. Let's do it the *REALLY* hard way.
204
+		foreach ($resultSet as $user) {
205
+			// We have to set this before doing OAuth queries. :(
206
+			$user->setDatabase($database);
207
+
208
+			// Don't use the cached data, but instead query the API.
209
+			if ($user->getOAuthOnWikiName(false) == $username) {
210
+				// Success.
211
+				return $user;
212
+			}
213
+		}
214
+
215
+		// Nope. Sorry.
216
+		return false;
217
+	}
218
+
219
+	/**
220
+	 * Gets a user by their OAuth request token
221
+	 *
222
+	 * @param string      $requestToken
223
+	 * @param PdoDatabase $database
224
+	 *
225
+	 * @return User|false
226
+	 */
227
+	public static function getByRequestToken($requestToken, PdoDatabase $database)
228
+	{
229
+		$statement = $database->prepare("SELECT * FROM user WHERE oauthrequesttoken = :id LIMIT 1;");
230
+		$statement->bindValue(":id", $requestToken);
231
+
232
+		$statement->execute();
233
+
234
+		$resultObject = $statement->fetchObject(get_called_class());
235
+
236
+		if ($resultObject != false) {
237
+			$resultObject->setDatabase($database);
238
+		}
239
+
240
+		return $resultObject;
241
+	}
242
+
243
+	/**
244
+	 * Gets all users with a supplied status
245
+	 *
246
+	 * @param string      $status
247
+	 * @param PdoDatabase $database
248
+	 *
249
+	 * @return User[]
250
+	 */
251
+	public static function getAllWithStatus($status, PdoDatabase $database)
252
+	{
253
+		$statement = $database->prepare("SELECT * FROM user WHERE status = :status");
254
+		$statement->execute(array(":status" => $status));
255
+
256
+		$resultObject = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
257
+
258
+		/** @var User $u */
259
+		foreach ($resultObject as $u) {
260
+			$u->setDatabase($database);
261
+		}
262
+
263
+		return $resultObject;
264
+	}
265
+
266
+	/**
267
+	 * Gets all checkusers
268
+	 *
269
+	 * @param PdoDatabase $database
270
+	 *
271
+	 * @return User[]
272
+	 */
273
+	public static function getAllCheckusers(PdoDatabase $database)
274
+	{
275
+		$statement = $database->prepare("SELECT * FROM user WHERE checkuser = 1;");
276
+		$statement->execute();
277
+
278
+		$resultObject = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
279
+
280
+		$resultsCollection = array();
281
+
282
+		/** @var User $u */
283
+		foreach ($resultObject as $u) {
284
+			$u->setDatabase($database);
285
+
286
+			if (!$u->isCheckuser()) {
287
+				continue;
288
+			}
289
+
290
+			$resultsCollection[] = $u;
291
+		}
292
+
293
+		return $resultsCollection;
294
+	}
295
+
296
+	/**
297
+	 * Gets all inactive users
298
+	 *
299
+	 * @param PdoDatabase $database
300
+	 *
301
+	 * @return User[]
302
+	 */
303
+	public static function getAllInactive(PdoDatabase $database)
304
+	{
305
+		$date = new DateTime();
306
+		$date->modify("-45 days");
307
+
308
+		$statement = $database->prepare(<<<SQL
309 309
 			SELECT * 
310 310
 			FROM user 
311 311
 			WHERE lastactive < :lastactivelimit 
@@ -314,104 +314,104 @@  discard block
 block discarded – undo
314 314
 				AND status != 'New' 
315 315
 			ORDER BY lastactive ASC;
316 316
 SQL
317
-        );
318
-        $statement->execute(array(":lastactivelimit" => $date->format("Y-m-d H:i:s")));
319
-
320
-        $resultObject = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
321
-
322
-        /** @var User $u */
323
-        foreach ($resultObject as $u) {
324
-            $u->setDatabase($database);
325
-        }
326
-
327
-        return $resultObject;
328
-    }
329
-
330
-    /**
331
-     * Gets all the usernames in the system
332
-     *
333
-     * @param PdoDatabase      $database
334
-     * @param null|bool|string $filter If null, no filter. If true, active users only, otherwise provided status.
335
-     *
336
-     * @return string[]
337
-     */
338
-    public static function getAllUsernames(PdoDatabase $database, $filter = null)
339
-    {
340
-        if ($filter === null) {
341
-            $userListQuery = "SELECT username FROM user;";
342
-            $userListResult = $database->query($userListQuery);
343
-        }
344
-        elseif ($filter === true) {
345
-            $userListQuery = "SELECT username FROM user WHERE status IN ('User', 'Admin');";
346
-            $userListResult = $database->query($userListQuery);
347
-        }
348
-        else {
349
-            $userListQuery = "SELECT username FROM user WHERE status = :status;";
350
-            $userListResult = $database->prepare($userListQuery);
351
-            $userListResult->execute(array(":status" => $filter));
352
-        }
353
-
354
-        return $userListResult->fetchAll(PDO::FETCH_COLUMN);
355
-    }
356
-
357
-    /**
358
-     * @param array       $userIds
359
-     * @param PdoDatabase $database
360
-     *
361
-     * @return array
362
-     * @throws Exception
363
-     */
364
-    public static function getUsernames($userIds, PdoDatabase $database)
365
-    {
366
-        if (!is_array($userIds)) {
367
-            throw new Exception('getUsernames() expects array');
368
-        }
369
-
370
-        if (count($userIds) === 0) {
371
-            // empty set of data
372
-            return array();
373
-        }
374
-
375
-        // Urgh. OK. You can't use IN() with parameters directly, so let's munge something together.
376
-        $userCount = count($userIds);
377
-
378
-        // Firstly, let's create a string of question marks, which will do as positional parameters.
379
-        $inSection = str_repeat('?,', $userCount - 1) . '?';
380
-
381
-        // Now, let's put that into the query. Direct string building here is OK, we're not dealing with user input,
382
-        // only the *count* of user input, which is safe.
383
-        $query = "SELECT id, username FROM user WHERE id IN ({$inSection})";
384
-
385
-        // Prepare the query
386
-        $statement = $database->prepare($query);
387
-
388
-        // Bind the parameters and execute - in one go - since we already have a handy array kicking around.
389
-        $statement->execute($userIds);
390
-
391
-        $resultSet = $statement->fetchAll(PDO::FETCH_ASSOC);
392
-
393
-        $users = array();
394
-        foreach ($resultSet as $row) {
395
-            $users[$row['id']] = $row['username'];
396
-        }
397
-
398
-        $statement->closeCursor();
399
-
400
-        return $users;
401
-    }
402
-
403
-    #endregion
404
-
405
-    /**
406
-     * Saves the current object
407
-     *
408
-     * @throws Exception
409
-     */
410
-    public function save()
411
-    {
412
-        if ($this->isNew()) {
413
-            // insert
414
-            $statement = $this->dbObject->prepare(<<<SQL
317
+		);
318
+		$statement->execute(array(":lastactivelimit" => $date->format("Y-m-d H:i:s")));
319
+
320
+		$resultObject = $statement->fetchAll(PDO::FETCH_CLASS, get_called_class());
321
+
322
+		/** @var User $u */
323
+		foreach ($resultObject as $u) {
324
+			$u->setDatabase($database);
325
+		}
326
+
327
+		return $resultObject;
328
+	}
329
+
330
+	/**
331
+	 * Gets all the usernames in the system
332
+	 *
333
+	 * @param PdoDatabase      $database
334
+	 * @param null|bool|string $filter If null, no filter. If true, active users only, otherwise provided status.
335
+	 *
336
+	 * @return string[]
337
+	 */
338
+	public static function getAllUsernames(PdoDatabase $database, $filter = null)
339
+	{
340
+		if ($filter === null) {
341
+			$userListQuery = "SELECT username FROM user;";
342
+			$userListResult = $database->query($userListQuery);
343
+		}
344
+		elseif ($filter === true) {
345
+			$userListQuery = "SELECT username FROM user WHERE status IN ('User', 'Admin');";
346
+			$userListResult = $database->query($userListQuery);
347
+		}
348
+		else {
349
+			$userListQuery = "SELECT username FROM user WHERE status = :status;";
350
+			$userListResult = $database->prepare($userListQuery);
351
+			$userListResult->execute(array(":status" => $filter));
352
+		}
353
+
354
+		return $userListResult->fetchAll(PDO::FETCH_COLUMN);
355
+	}
356
+
357
+	/**
358
+	 * @param array       $userIds
359
+	 * @param PdoDatabase $database
360
+	 *
361
+	 * @return array
362
+	 * @throws Exception
363
+	 */
364
+	public static function getUsernames($userIds, PdoDatabase $database)
365
+	{
366
+		if (!is_array($userIds)) {
367
+			throw new Exception('getUsernames() expects array');
368
+		}
369
+
370
+		if (count($userIds) === 0) {
371
+			// empty set of data
372
+			return array();
373
+		}
374
+
375
+		// Urgh. OK. You can't use IN() with parameters directly, so let's munge something together.
376
+		$userCount = count($userIds);
377
+
378
+		// Firstly, let's create a string of question marks, which will do as positional parameters.
379
+		$inSection = str_repeat('?,', $userCount - 1) . '?';
380
+
381
+		// Now, let's put that into the query. Direct string building here is OK, we're not dealing with user input,
382
+		// only the *count* of user input, which is safe.
383
+		$query = "SELECT id, username FROM user WHERE id IN ({$inSection})";
384
+
385
+		// Prepare the query
386
+		$statement = $database->prepare($query);
387
+
388
+		// Bind the parameters and execute - in one go - since we already have a handy array kicking around.
389
+		$statement->execute($userIds);
390
+
391
+		$resultSet = $statement->fetchAll(PDO::FETCH_ASSOC);
392
+
393
+		$users = array();
394
+		foreach ($resultSet as $row) {
395
+			$users[$row['id']] = $row['username'];
396
+		}
397
+
398
+		$statement->closeCursor();
399
+
400
+		return $users;
401
+	}
402
+
403
+	#endregion
404
+
405
+	/**
406
+	 * Saves the current object
407
+	 *
408
+	 * @throws Exception
409
+	 */
410
+	public function save()
411
+	{
412
+		if ($this->isNew()) {
413
+			// insert
414
+			$statement = $this->dbObject->prepare(<<<SQL
415 415
 				INSERT INTO `user` ( 
416 416
 					username, email, password, status, onwikiname, welcome_sig, 
417 417
 					lastactive, forcelogout, checkuser, forceidentified,
@@ -425,36 +425,36 @@  discard block
 block discarded – undo
425 425
 					:ort, :ors, :oat, :oas
426 426
 				);
427 427
 SQL
428
-            );
429
-            $statement->bindValue(":username", $this->username);
430
-            $statement->bindValue(":email", $this->email);
431
-            $statement->bindValue(":password", $this->password);
432
-            $statement->bindValue(":status", $this->status);
433
-            $statement->bindValue(":onwikiname", $this->onwikiname);
434
-            $statement->bindValue(":welcome_sig", $this->welcome_sig);
435
-            $statement->bindValue(":lastactive", $this->lastactive);
436
-            $statement->bindValue(":forcelogout", $this->forcelogout);
437
-            $statement->bindValue(":checkuser", $this->checkuser);
438
-            $statement->bindValue(":forceidentified", $this->forceidentified);
439
-            $statement->bindValue(":welcome_template", $this->welcome_template);
440
-            $statement->bindValue(":abortpref", $this->abortpref);
441
-            $statement->bindValue(":confirmationdiff", $this->confirmationdiff);
442
-            $statement->bindValue(":emailsig", $this->emailsig);
443
-            $statement->bindValue(":ort", $this->oauthrequesttoken);
444
-            $statement->bindValue(":ors", $this->oauthrequestsecret);
445
-            $statement->bindValue(":oat", $this->oauthaccesstoken);
446
-            $statement->bindValue(":oas", $this->oauthaccesssecret);
447
-
448
-            if ($statement->execute()) {
449
-                $this->id = (int)$this->dbObject->lastInsertId();
450
-            }
451
-            else {
452
-                throw new Exception($statement->errorInfo());
453
-            }
454
-        }
455
-        else {
456
-            // update
457
-            $statement = $this->dbObject->prepare(<<<SQL
428
+			);
429
+			$statement->bindValue(":username", $this->username);
430
+			$statement->bindValue(":email", $this->email);
431
+			$statement->bindValue(":password", $this->password);
432
+			$statement->bindValue(":status", $this->status);
433
+			$statement->bindValue(":onwikiname", $this->onwikiname);
434
+			$statement->bindValue(":welcome_sig", $this->welcome_sig);
435
+			$statement->bindValue(":lastactive", $this->lastactive);
436
+			$statement->bindValue(":forcelogout", $this->forcelogout);
437
+			$statement->bindValue(":checkuser", $this->checkuser);
438
+			$statement->bindValue(":forceidentified", $this->forceidentified);
439
+			$statement->bindValue(":welcome_template", $this->welcome_template);
440
+			$statement->bindValue(":abortpref", $this->abortpref);
441
+			$statement->bindValue(":confirmationdiff", $this->confirmationdiff);
442
+			$statement->bindValue(":emailsig", $this->emailsig);
443
+			$statement->bindValue(":ort", $this->oauthrequesttoken);
444
+			$statement->bindValue(":ors", $this->oauthrequestsecret);
445
+			$statement->bindValue(":oat", $this->oauthaccesstoken);
446
+			$statement->bindValue(":oas", $this->oauthaccesssecret);
447
+
448
+			if ($statement->execute()) {
449
+				$this->id = (int)$this->dbObject->lastInsertId();
450
+			}
451
+			else {
452
+				throw new Exception($statement->errorInfo());
453
+			}
454
+		}
455
+		else {
456
+			// update
457
+			$statement = $this->dbObject->prepare(<<<SQL
458 458
 				UPDATE `user` SET 
459 459
 					username = :username, email = :email, 
460 460
 					password = :password, status = :status,
@@ -469,721 +469,721 @@  discard block
 block discarded – undo
469 469
 				WHERE id = :id AND updateversion = :updateversion
470 470
 				LIMIT 1;
471 471
 SQL
472
-            );
473
-            $statement->bindValue(":forceidentified", $this->forceidentified);
474
-
475
-            $statement->bindValue(':id', $this->id);
476
-            $statement->bindValue(':updateversion', $this->updateversion);
477
-
478
-            $statement->bindValue(':username', $this->username);
479
-            $statement->bindValue(':email', $this->email);
480
-            $statement->bindValue(':password', $this->password);
481
-            $statement->bindValue(':status', $this->status);
482
-            $statement->bindValue(':onwikiname', $this->onwikiname);
483
-            $statement->bindValue(':welcome_sig', $this->welcome_sig);
484
-            $statement->bindValue(':lastactive', $this->lastactive);
485
-            $statement->bindValue(':forcelogout', $this->forcelogout);
486
-            $statement->bindValue(':checkuser', $this->checkuser);
487
-            $statement->bindValue(':forceidentified', $this->forceidentified);
488
-            $statement->bindValue(':welcome_template', $this->welcome_template);
489
-            $statement->bindValue(':abortpref', $this->abortpref);
490
-            $statement->bindValue(':confirmationdiff', $this->confirmationdiff);
491
-            $statement->bindValue(':emailsig', $this->emailsig);
492
-            $statement->bindValue(':ort', $this->oauthrequesttoken);
493
-            $statement->bindValue(':ors', $this->oauthrequestsecret);
494
-            $statement->bindValue(':oat', $this->oauthaccesstoken);
495
-            $statement->bindValue(':oas', $this->oauthaccesssecret);
496
-
497
-            if (!$statement->execute()) {
498
-                throw new Exception($statement->errorInfo());
499
-            }
500
-
501
-            if ($statement->rowCount() !== 1) {
502
-                throw new OptimisticLockFailedException();
503
-            }
504
-
505
-            $this->updateversion++;
506
-        }
507
-    }
508
-
509
-    /**
510
-     * Authenticates the user with the supplied password
511
-     *
512
-     * @param string $password
513
-     *
514
-     * @return bool
515
-     * @throws Exception
516
-     * @category Security-Critical
517
-     */
518
-    public function authenticate($password)
519
-    {
520
-        $result = AuthUtility::testCredentials($password, $this->password);
521
-
522
-        if ($result === true) {
523
-            // password version is out of date, update it.
524
-            if (!AuthUtility::isCredentialVersionLatest($this->password)) {
525
-                $this->password = AuthUtility::encryptPassword($password);
526
-                $this->save();
527
-            }
528
-        }
529
-
530
-        return $result;
531
-    }
532
-
533
-    #region properties
534
-
535
-    /**
536
-     * Gets the tool username
537
-     * @return string
538
-     */
539
-    public function getUsername()
540
-    {
541
-        return $this->username;
542
-    }
543
-
544
-    /**
545
-     * Sets the tool username
546
-     *
547
-     * @param string $username
548
-     */
549
-    public function setUsername($username)
550
-    {
551
-        $this->username = $username;
552
-
553
-        // If this isn't a brand new user, then it's a rename, force the logout
554
-        if (!$this->isNew()) {
555
-            $this->forcelogout = 1;
556
-        }
557
-    }
558
-
559
-    /**
560
-     * Gets the user's email address
561
-     * @return string
562
-     */
563
-    public function getEmail()
564
-    {
565
-        return $this->email;
566
-    }
567
-
568
-    /**
569
-     * Sets the user's email address
570
-     *
571
-     * @param string $email
572
-     */
573
-    public function setEmail($email)
574
-    {
575
-        $this->email = $email;
576
-    }
577
-
578
-    /**
579
-     * Sets the user's password
580
-     *
581
-     * @param string $password the plaintext password
582
-     *
583
-     * @category Security-Critical
584
-     */
585
-    public function setPassword($password)
586
-    {
587
-        $this->password = AuthUtility::encryptPassword($password);
588
-    }
589
-
590
-    /**
591
-     * Gets the status (User, Admin, Suspended, etc - excludes checkuser) of the user.
592
-     * @return string
593
-     */
594
-    public function getStatus()
595
-    {
596
-        return $this->status;
597
-    }
598
-
599
-    /**
600
-     * @param string $status
601
-     */
602
-    public function setStatus($status)
603
-    {
604
-        $this->status = $status;
605
-    }
606
-
607
-    /**
608
-     * Gets the user's on-wiki name
609
-     * @return string
610
-     */
611
-    public function getOnWikiName()
612
-    {
613
-        if ($this->oauthaccesstoken !== null) {
614
-            try {
615
-                return $this->getOAuthOnWikiName();
616
-            }
617
-            catch (Exception $ex) {
618
-                // urm.. log this?
619
-                return $this->onwikiname;
620
-            }
621
-        }
622
-
623
-        return $this->onwikiname;
624
-    }
625
-
626
-    /**
627
-     * This is probably NOT the function you want!
628
-     *
629
-     * Take a look at getOnWikiName() instead.
630
-     * @return string
631
-     */
632
-    public function getStoredOnWikiName()
633
-    {
634
-        return $this->onwikiname;
635
-    }
636
-
637
-    /**
638
-     * Sets the user's on-wiki name
639
-     *
640
-     * This can have interesting side-effects with OAuth.
641
-     *
642
-     * @param string $onWikiName
643
-     */
644
-    public function setOnWikiName($onWikiName)
645
-    {
646
-        $this->onwikiname = $onWikiName;
647
-    }
648
-
649
-    /**
650
-     * Gets the welcome signature
651
-     * @return string
652
-     */
653
-    public function getWelcomeSig()
654
-    {
655
-        return $this->welcome_sig;
656
-    }
657
-
658
-    /**
659
-     * Sets the welcome signature
660
-     *
661
-     * @param string $welcomeSig
662
-     */
663
-    public function setWelcomeSig($welcomeSig)
664
-    {
665
-        $this->welcome_sig = $welcomeSig;
666
-    }
667
-
668
-    /**
669
-     * Gets the last activity date for the user
670
-     *
671
-     * @return string
672
-     * @todo This should probably return an instance of DateTime
673
-     */
674
-    public function getLastActive()
675
-    {
676
-        return $this->lastactive;
677
-    }
678
-
679
-    /**
680
-     * Gets the user's forced logout status
681
-     *
682
-     * @return bool
683
-     */
684
-    public function getForceLogout()
685
-    {
686
-        return $this->forcelogout == 1;
687
-    }
688
-
689
-    /**
690
-     * Sets the user's forced logout status
691
-     *
692
-     * @param bool $forceLogout
693
-     */
694
-    public function setForceLogout($forceLogout)
695
-    {
696
-        $this->forcelogout = $forceLogout ? 1 : 0;
697
-    }
698
-
699
-    /**
700
-     * Returns the ID of the welcome template used.
701
-     * @return int
702
-     */
703
-    public function getWelcomeTemplate()
704
-    {
705
-        return $this->welcome_template;
706
-    }
707
-
708
-    /**
709
-     * Sets the ID of the welcome template used.
710
-     *
711
-     * @param int $welcomeTemplate
712
-     */
713
-    public function setWelcomeTemplate($welcomeTemplate)
714
-    {
715
-        $this->welcome_template = $welcomeTemplate;
716
-    }
717
-
718
-    /**
719
-     * Gets the user's abort preference
720
-     * @todo this is badly named too! Also a bool that's actually an int.
721
-     * @return int
722
-     */
723
-    public function getAbortPref()
724
-    {
725
-        return $this->abortpref;
726
-    }
727
-
728
-    /**
729
-     * Sets the user's abort preference
730
-     * @todo rename, retype, and re-comment.
731
-     *
732
-     * @param int $abortPreference
733
-     */
734
-    public function setAbortPref($abortPreference)
735
-    {
736
-        $this->abortpref = $abortPreference;
737
-    }
738
-
739
-    /**
740
-     * Gets the user's confirmation diff. Unused if OAuth is in use.
741
-     * @return int the diff ID
742
-     */
743
-    public function getConfirmationDiff()
744
-    {
745
-        return $this->confirmationdiff;
746
-    }
747
-
748
-    /**
749
-     * Sets the user's confirmation diff.
750
-     *
751
-     * @param int $confirmationDiff
752
-     */
753
-    public function setConfirmationDiff($confirmationDiff)
754
-    {
755
-        $this->confirmationdiff = $confirmationDiff;
756
-    }
757
-
758
-    /**
759
-     * Gets the users' email signature used on outbound mail.
760
-     * @todo rename me!
761
-     * @return string
762
-     */
763
-    public function getEmailSig()
764
-    {
765
-        return $this->emailsig;
766
-    }
767
-
768
-    /**
769
-     * Sets the user's email signature for outbound mail.
770
-     *
771
-     * @param string $emailSignature
772
-     */
773
-    public function setEmailSig($emailSignature)
774
-    {
775
-        $this->emailsig = $emailSignature;
776
-    }
777
-
778
-    /**
779
-     * Gets the user's OAuth request token.
780
-     *
781
-     * @todo move me to a collaborator.
782
-     * @return null|string
783
-     */
784
-    public function getOAuthRequestToken()
785
-    {
786
-        return $this->oauthrequesttoken;
787
-    }
788
-
789
-    /**
790
-     * Sets the user's OAuth request token
791
-     * @todo move me to a collaborator
792
-     *
793
-     * @param string $oAuthRequestToken
794
-     */
795
-    public function setOAuthRequestToken($oAuthRequestToken)
796
-    {
797
-        $this->oauthrequesttoken = $oAuthRequestToken;
798
-    }
799
-
800
-    /**
801
-     * Gets the users OAuth request secret
802
-     * @category Security-Critical
803
-     * @todo     move me to a collaborator
804
-     * @return null|string
805
-     */
806
-    public function getOAuthRequestSecret()
807
-    {
808
-        return $this->oauthrequestsecret;
809
-    }
810
-
811
-    /**
812
-     * Sets the user's OAuth request secret
813
-     * @todo move me to a collaborator
814
-     *
815
-     * @param string $oAuthRequestSecret
816
-     */
817
-    public function setOAuthRequestSecret($oAuthRequestSecret)
818
-    {
819
-        $this->oauthrequestsecret = $oAuthRequestSecret;
820
-    }
821
-
822
-    /**
823
-     * Gets the user's access token
824
-     * @category Security-Critical
825
-     * @todo     move me to a collaborator
826
-     * @return null|string
827
-     */
828
-    public function getOAuthAccessToken()
829
-    {
830
-        return $this->oauthaccesstoken;
831
-    }
832
-
833
-    /**
834
-     * Sets the user's access token
835
-     * @todo move me to a collaborator
836
-     *
837
-     * @param string $oAuthAccessToken
838
-     */
839
-    public function setOAuthAccessToken($oAuthAccessToken)
840
-    {
841
-        $this->oauthaccesstoken = $oAuthAccessToken;
842
-    }
843
-
844
-    /**
845
-     * Gets the user's OAuth access secret
846
-     * @category Security-Critical
847
-     * @todo     move me to a collaborator
848
-     * @return null|string
849
-     */
850
-    public function getOAuthAccessSecret()
851
-    {
852
-        return $this->oauthaccesssecret;
853
-    }
854
-
855
-    /**
856
-     * Sets the user's OAuth access secret
857
-     * @todo move me to a collaborator
858
-     *
859
-     * @param string $oAuthAccessSecret
860
-     */
861
-    public function setOAuthAccessSecret($oAuthAccessSecret)
862
-    {
863
-        $this->oauthaccesssecret = $oAuthAccessSecret;
864
-    }
865
-
866
-    #endregion
867
-
868
-    #region user access checks
869
-
870
-    /**
871
-     * Tests if the user is an admin
872
-     * @return bool
873
-     * @category Security-Critical
874
-     */
875
-    public function isAdmin()
876
-    {
877
-        return $this->status == self::STATUS_ADMIN;
878
-    }
879
-
880
-    /**
881
-     * Tests if the user is a checkuser
882
-     * @return bool
883
-     * @category Security-Critical
884
-     */
885
-    public function isCheckuser()
886
-    {
887
-        return $this->checkuser == 1 || $this->oauthCanCheckUser();
888
-    }
889
-
890
-    /**
891
-     * Tests if the user is identified
892
-     *
893
-     * @param IdentificationVerifier $iv
894
-     *
895
-     * @return bool
896
-     * @todo     Figure out what on earth is going on with PDO's typecasting here.  Apparently, it returns string("0") for
897
-     *       the force-unidentified case, and int(1) for the identified case?!  This is quite ugly, but probably needed
898
-     *       to play it safe for now.
899
-     * @category Security-Critical
900
-     */
901
-    public function isIdentified(IdentificationVerifier $iv)
902
-    {
903
-        if ($this->forceidentified === 0 || $this->forceidentified === "0") {
904
-            // User forced to unidentified in the database.
905
-            return false;
906
-        }
907
-        elseif ($this->forceidentified === 1 || $this->forceidentified === "1") {
908
-            // User forced to identified in the database.
909
-            return true;
910
-        }
911
-        else {
912
-            // User not forced to any particular identified status; consult IdentificationVerifier
913
-            return $iv->isUserIdentified($this->getOnWikiName());
914
-        }
915
-    }
916
-
917
-    /**
918
-     * Tests if the user is suspended
919
-     * @return bool
920
-     * @category Security-Critical
921
-     */
922
-    public function isSuspended()
923
-    {
924
-        return $this->status == self::STATUS_SUSPENDED;
925
-    }
926
-
927
-    /**
928
-     * Tests if the user is new
929
-     * @return bool
930
-     * @category Security-Critical
931
-     */
932
-    public function isNewUser()
933
-    {
934
-        return $this->status == self::STATUS_NEW;
935
-    }
936
-
937
-    /**
938
-     * Tests if the user is a standard-level user
939
-     * @return bool
940
-     * @category Security-Critical
941
-     */
942
-    public function isUser()
943
-    {
944
-        return $this->status == self::STATUS_USER;
945
-    }
946
-
947
-    /**
948
-     * Tests if the user has been declined access to the tool
949
-     * @return bool
950
-     * @category Security-Critical
951
-     */
952
-    public function isDeclined()
953
-    {
954
-        return $this->status == self::STATUS_DECLINED;
955
-    }
956
-
957
-    /**
958
-     * Tests if the user is the community user
959
-     *
960
-     * @todo     decide if this means logged out. I think it usually does.
961
-     * @return bool
962
-     * @category Security-Critical
963
-     */
964
-    public function isCommunityUser()
965
-    {
966
-        return false;
967
-    }
968
-
969
-    #endregion 
970
-
971
-    #region OAuth
972
-
973
-    /**
974
-     * @todo     move me to a collaborator
975
-     *
976
-     * @param bool $useCached
977
-     *
978
-     * @return mixed|null
979
-     * @category Security-Critical
980
-     */
981
-    public function getOAuthIdentity($useCached = false)
982
-    {
983
-        if ($this->oauthaccesstoken === null) {
984
-            $this->clearOAuthData();
985
-        }
986
-
987
-        global $oauthConsumerToken, $oauthMediaWikiCanonicalServer;
988
-
989
-        if ($this->oauthidentitycache == null) {
990
-            $this->identityCache = null;
991
-        }
992
-        else {
993
-            $this->identityCache = unserialize($this->oauthidentitycache);
994
-        }
995
-
996
-        // check the cache
997
-        if (
998
-            $this->identityCache != null &&
999
-            $this->identityCache->aud == $oauthConsumerToken &&
1000
-            $this->identityCache->iss == $oauthMediaWikiCanonicalServer
1001
-        ) {
1002
-            if (
1003
-                $useCached || (
1004
-                    DateTime::createFromFormat("U", $this->identityCache->iat) < new DateTime() &&
1005
-                    DateTime::createFromFormat("U", $this->identityCache->exp) > new DateTime()
1006
-                )
1007
-            ) {
1008
-                // Use cached value - it's either valid or we don't care.
1009
-                return $this->identityCache;
1010
-            }
1011
-            else {
1012
-                // Cache expired and not forcing use of cached value
1013
-                $this->getIdentityCache();
1014
-
1015
-                return $this->identityCache;
1016
-            }
1017
-        }
1018
-        else {
1019
-            // Cache isn't ours or doesn't exist
1020
-            $this->getIdentityCache();
1021
-
1022
-            return $this->identityCache;
1023
-        }
1024
-    }
1025
-
1026
-    /**
1027
-     * @todo     move me to a collaborator
1028
-     *
1029
-     * @param mixed $useCached Set to false for everything where up-to-date data is important.
1030
-     *
1031
-     * @return mixed
1032
-     * @category Security-Critical
1033
-     */
1034
-    private function getOAuthOnWikiName($useCached = false)
1035
-    {
1036
-        $identity = $this->getOAuthIdentity($useCached);
1037
-        if ($identity !== null) {
1038
-            return $identity->username;
1039
-        }
1040
-
1041
-        return false;
1042
-    }
1043
-
1044
-    /**
1045
-     * @return bool
1046
-     * @todo move me to a collaborator
1047
-     */
1048
-    public function isOAuthLinked()
1049
-    {
1050
-        if ($this->onwikiname === "##OAUTH##") {
1051
-            return true; // special value. If an account must be oauth linked, this is true.
1052
-        }
1053
-
1054
-        return $this->oauthaccesstoken !== null;
1055
-    }
1056
-
1057
-    /**
1058
-     * @return null
1059
-     * @todo move me to a collaborator
1060
-     */
1061
-    public function clearOAuthData()
1062
-    {
1063
-        $this->identityCache = null;
1064
-        $this->oauthidentitycache = null;
1065
-        $clearCacheQuery = "UPDATE user SET oauthidentitycache = NULL WHERE id = :id;";
1066
-        $this->dbObject->prepare($clearCacheQuery)->execute(array(":id" => $this->id));
1067
-
1068
-        return null;
1069
-    }
1070
-
1071
-    /**
1072
-     * @throws Exception
1073
-     * @todo     move me to a collaborator
1074
-     * @category Security-Critical
1075
-     */
1076
-    private function getIdentityCache()
1077
-    {
1078
-        /** @var IOAuthHelper $oauthHelper */
1079
-        global $oauthHelper;
1080
-
1081
-        try {
1082
-            $this->identityCache = $oauthHelper->getIdentityTicket($this->oauthaccesstoken, $this->oauthaccesssecret);
1083
-
1084
-            $this->oauthidentitycache = serialize($this->identityCache);
1085
-            $this->dbObject->prepare("UPDATE user SET oauthidentitycache = :identity WHERE id = :id;")
1086
-                ->execute(array(":id" => $this->id, ":identity" => $this->oauthidentitycache));
1087
-        }
1088
-        catch (UnexpectedValueException $ex) {
1089
-            $this->identityCache = null;
1090
-            $this->oauthidentitycache = null;
1091
-            $this->dbObject->prepare("UPDATE user SET oauthidentitycache = NULL WHERE id = :id;")
1092
-                ->execute(array(":id" => $this->id));
1093
-
1094
-            SessionAlert::warning("OAuth error getting identity from MediaWiki: " . $ex->getMessage());
1095
-        }
1096
-    }
1097
-
1098
-    /**
1099
-     * @return bool
1100
-     * @todo move me to a collaborator
1101
-     */
1102
-    public function oauthCanUse()
1103
-    {
1104
-        try {
1105
-            return in_array('useoauth', $this->getOAuthIdentity()->grants);
1106
-        }
1107
-        catch (Exception $ex) {
1108
-            return false;
1109
-        }
1110
-    }
1111
-
1112
-    /**
1113
-     * @return bool
1114
-     * @todo move me to a collaborator
1115
-     */
1116
-    public function oauthCanEdit()
1117
-    {
1118
-        try {
1119
-            return in_array('useoauth', $this->getOAuthIdentity()->grants)
1120
-            && in_array('createeditmovepage', $this->getOAuthIdentity()->grants)
1121
-            && in_array('createtalk', $this->getOAuthIdentity()->rights)
1122
-            && in_array('edit', $this->getOAuthIdentity()->rights)
1123
-            && in_array('writeapi', $this->getOAuthIdentity()->rights);
1124
-        }
1125
-        catch (Exception $ex) {
1126
-            return false;
1127
-        }
1128
-    }
1129
-
1130
-    /**
1131
-     * @return bool
1132
-     * @todo move me to a collaborator
1133
-     */
1134
-    public function oauthCanCreateAccount()
1135
-    {
1136
-        try {
1137
-            return in_array('useoauth', $this->getOAuthIdentity()->grants)
1138
-            && in_array('createaccount', $this->getOAuthIdentity()->grants)
1139
-            && in_array('createaccount', $this->getOAuthIdentity()->rights)
1140
-            && in_array('writeapi', $this->getOAuthIdentity()->rights);
1141
-        }
1142
-        catch (Exception $ex) {
1143
-            return false;
1144
-        }
1145
-    }
1146
-
1147
-    /**
1148
-     * @return bool
1149
-     * @todo     move me to a collaborator
1150
-     * @category Security-Critical
1151
-     */
1152
-    protected function oauthCanCheckUser()
1153
-    {
1154
-        if (!$this->isOAuthLinked()) {
1155
-            return false;
1156
-        }
1157
-
1158
-        try {
1159
-            $identity = $this->getOAuthIdentity();
1160
-
1161
-            return in_array('checkuser', $identity->rights);
1162
-        }
1163
-        catch (Exception $ex) {
1164
-            return false;
1165
-        }
1166
-    }
1167
-
1168
-    #endregion
1169
-
1170
-    /**
1171
-     * Gets a hash of data for the user to reset their password with.
1172
-     * @category Security-Critical
1173
-     * @return string
1174
-     */
1175
-    public function getForgottenPasswordHash()
1176
-    {
1177
-        return md5($this->username . $this->email . $this->welcome_template . $this->id . $this->password);
1178
-    }
1179
-
1180
-    /**
1181
-     * Gets the approval date of the user
1182
-     * @return DateTime|false
1183
-     */
1184
-    public function getApprovalDate()
1185
-    {
1186
-        $query = $this->dbObject->prepare(<<<SQL
472
+			);
473
+			$statement->bindValue(":forceidentified", $this->forceidentified);
474
+
475
+			$statement->bindValue(':id', $this->id);
476
+			$statement->bindValue(':updateversion', $this->updateversion);
477
+
478
+			$statement->bindValue(':username', $this->username);
479
+			$statement->bindValue(':email', $this->email);
480
+			$statement->bindValue(':password', $this->password);
481
+			$statement->bindValue(':status', $this->status);
482
+			$statement->bindValue(':onwikiname', $this->onwikiname);
483
+			$statement->bindValue(':welcome_sig', $this->welcome_sig);
484
+			$statement->bindValue(':lastactive', $this->lastactive);
485
+			$statement->bindValue(':forcelogout', $this->forcelogout);
486
+			$statement->bindValue(':checkuser', $this->checkuser);
487
+			$statement->bindValue(':forceidentified', $this->forceidentified);
488
+			$statement->bindValue(':welcome_template', $this->welcome_template);
489
+			$statement->bindValue(':abortpref', $this->abortpref);
490
+			$statement->bindValue(':confirmationdiff', $this->confirmationdiff);
491
+			$statement->bindValue(':emailsig', $this->emailsig);
492
+			$statement->bindValue(':ort', $this->oauthrequesttoken);
493
+			$statement->bindValue(':ors', $this->oauthrequestsecret);
494
+			$statement->bindValue(':oat', $this->oauthaccesstoken);
495
+			$statement->bindValue(':oas', $this->oauthaccesssecret);
496
+
497
+			if (!$statement->execute()) {
498
+				throw new Exception($statement->errorInfo());
499
+			}
500
+
501
+			if ($statement->rowCount() !== 1) {
502
+				throw new OptimisticLockFailedException();
503
+			}
504
+
505
+			$this->updateversion++;
506
+		}
507
+	}
508
+
509
+	/**
510
+	 * Authenticates the user with the supplied password
511
+	 *
512
+	 * @param string $password
513
+	 *
514
+	 * @return bool
515
+	 * @throws Exception
516
+	 * @category Security-Critical
517
+	 */
518
+	public function authenticate($password)
519
+	{
520
+		$result = AuthUtility::testCredentials($password, $this->password);
521
+
522
+		if ($result === true) {
523
+			// password version is out of date, update it.
524
+			if (!AuthUtility::isCredentialVersionLatest($this->password)) {
525
+				$this->password = AuthUtility::encryptPassword($password);
526
+				$this->save();
527
+			}
528
+		}
529
+
530
+		return $result;
531
+	}
532
+
533
+	#region properties
534
+
535
+	/**
536
+	 * Gets the tool username
537
+	 * @return string
538
+	 */
539
+	public function getUsername()
540
+	{
541
+		return $this->username;
542
+	}
543
+
544
+	/**
545
+	 * Sets the tool username
546
+	 *
547
+	 * @param string $username
548
+	 */
549
+	public function setUsername($username)
550
+	{
551
+		$this->username = $username;
552
+
553
+		// If this isn't a brand new user, then it's a rename, force the logout
554
+		if (!$this->isNew()) {
555
+			$this->forcelogout = 1;
556
+		}
557
+	}
558
+
559
+	/**
560
+	 * Gets the user's email address
561
+	 * @return string
562
+	 */
563
+	public function getEmail()
564
+	{
565
+		return $this->email;
566
+	}
567
+
568
+	/**
569
+	 * Sets the user's email address
570
+	 *
571
+	 * @param string $email
572
+	 */
573
+	public function setEmail($email)
574
+	{
575
+		$this->email = $email;
576
+	}
577
+
578
+	/**
579
+	 * Sets the user's password
580
+	 *
581
+	 * @param string $password the plaintext password
582
+	 *
583
+	 * @category Security-Critical
584
+	 */
585
+	public function setPassword($password)
586
+	{
587
+		$this->password = AuthUtility::encryptPassword($password);
588
+	}
589
+
590
+	/**
591
+	 * Gets the status (User, Admin, Suspended, etc - excludes checkuser) of the user.
592
+	 * @return string
593
+	 */
594
+	public function getStatus()
595
+	{
596
+		return $this->status;
597
+	}
598
+
599
+	/**
600
+	 * @param string $status
601
+	 */
602
+	public function setStatus($status)
603
+	{
604
+		$this->status = $status;
605
+	}
606
+
607
+	/**
608
+	 * Gets the user's on-wiki name
609
+	 * @return string
610
+	 */
611
+	public function getOnWikiName()
612
+	{
613
+		if ($this->oauthaccesstoken !== null) {
614
+			try {
615
+				return $this->getOAuthOnWikiName();
616
+			}
617
+			catch (Exception $ex) {
618
+				// urm.. log this?
619
+				return $this->onwikiname;
620
+			}
621
+		}
622
+
623
+		return $this->onwikiname;
624
+	}
625
+
626
+	/**
627
+	 * This is probably NOT the function you want!
628
+	 *
629
+	 * Take a look at getOnWikiName() instead.
630
+	 * @return string
631
+	 */
632
+	public function getStoredOnWikiName()
633
+	{
634
+		return $this->onwikiname;
635
+	}
636
+
637
+	/**
638
+	 * Sets the user's on-wiki name
639
+	 *
640
+	 * This can have interesting side-effects with OAuth.
641
+	 *
642
+	 * @param string $onWikiName
643
+	 */
644
+	public function setOnWikiName($onWikiName)
645
+	{
646
+		$this->onwikiname = $onWikiName;
647
+	}
648
+
649
+	/**
650
+	 * Gets the welcome signature
651
+	 * @return string
652
+	 */
653
+	public function getWelcomeSig()
654
+	{
655
+		return $this->welcome_sig;
656
+	}
657
+
658
+	/**
659
+	 * Sets the welcome signature
660
+	 *
661
+	 * @param string $welcomeSig
662
+	 */
663
+	public function setWelcomeSig($welcomeSig)
664
+	{
665
+		$this->welcome_sig = $welcomeSig;
666
+	}
667
+
668
+	/**
669
+	 * Gets the last activity date for the user
670
+	 *
671
+	 * @return string
672
+	 * @todo This should probably return an instance of DateTime
673
+	 */
674
+	public function getLastActive()
675
+	{
676
+		return $this->lastactive;
677
+	}
678
+
679
+	/**
680
+	 * Gets the user's forced logout status
681
+	 *
682
+	 * @return bool
683
+	 */
684
+	public function getForceLogout()
685
+	{
686
+		return $this->forcelogout == 1;
687
+	}
688
+
689
+	/**
690
+	 * Sets the user's forced logout status
691
+	 *
692
+	 * @param bool $forceLogout
693
+	 */
694
+	public function setForceLogout($forceLogout)
695
+	{
696
+		$this->forcelogout = $forceLogout ? 1 : 0;
697
+	}
698
+
699
+	/**
700
+	 * Returns the ID of the welcome template used.
701
+	 * @return int
702
+	 */
703
+	public function getWelcomeTemplate()
704
+	{
705
+		return $this->welcome_template;
706
+	}
707
+
708
+	/**
709
+	 * Sets the ID of the welcome template used.
710
+	 *
711
+	 * @param int $welcomeTemplate
712
+	 */
713
+	public function setWelcomeTemplate($welcomeTemplate)
714
+	{
715
+		$this->welcome_template = $welcomeTemplate;
716
+	}
717
+
718
+	/**
719
+	 * Gets the user's abort preference
720
+	 * @todo this is badly named too! Also a bool that's actually an int.
721
+	 * @return int
722
+	 */
723
+	public function getAbortPref()
724
+	{
725
+		return $this->abortpref;
726
+	}
727
+
728
+	/**
729
+	 * Sets the user's abort preference
730
+	 * @todo rename, retype, and re-comment.
731
+	 *
732
+	 * @param int $abortPreference
733
+	 */
734
+	public function setAbortPref($abortPreference)
735
+	{
736
+		$this->abortpref = $abortPreference;
737
+	}
738
+
739
+	/**
740
+	 * Gets the user's confirmation diff. Unused if OAuth is in use.
741
+	 * @return int the diff ID
742
+	 */
743
+	public function getConfirmationDiff()
744
+	{
745
+		return $this->confirmationdiff;
746
+	}
747
+
748
+	/**
749
+	 * Sets the user's confirmation diff.
750
+	 *
751
+	 * @param int $confirmationDiff
752
+	 */
753
+	public function setConfirmationDiff($confirmationDiff)
754
+	{
755
+		$this->confirmationdiff = $confirmationDiff;
756
+	}
757
+
758
+	/**
759
+	 * Gets the users' email signature used on outbound mail.
760
+	 * @todo rename me!
761
+	 * @return string
762
+	 */
763
+	public function getEmailSig()
764
+	{
765
+		return $this->emailsig;
766
+	}
767
+
768
+	/**
769
+	 * Sets the user's email signature for outbound mail.
770
+	 *
771
+	 * @param string $emailSignature
772
+	 */
773
+	public function setEmailSig($emailSignature)
774
+	{
775
+		$this->emailsig = $emailSignature;
776
+	}
777
+
778
+	/**
779
+	 * Gets the user's OAuth request token.
780
+	 *
781
+	 * @todo move me to a collaborator.
782
+	 * @return null|string
783
+	 */
784
+	public function getOAuthRequestToken()
785
+	{
786
+		return $this->oauthrequesttoken;
787
+	}
788
+
789
+	/**
790
+	 * Sets the user's OAuth request token
791
+	 * @todo move me to a collaborator
792
+	 *
793
+	 * @param string $oAuthRequestToken
794
+	 */
795
+	public function setOAuthRequestToken($oAuthRequestToken)
796
+	{
797
+		$this->oauthrequesttoken = $oAuthRequestToken;
798
+	}
799
+
800
+	/**
801
+	 * Gets the users OAuth request secret
802
+	 * @category Security-Critical
803
+	 * @todo     move me to a collaborator
804
+	 * @return null|string
805
+	 */
806
+	public function getOAuthRequestSecret()
807
+	{
808
+		return $this->oauthrequestsecret;
809
+	}
810
+
811
+	/**
812
+	 * Sets the user's OAuth request secret
813
+	 * @todo move me to a collaborator
814
+	 *
815
+	 * @param string $oAuthRequestSecret
816
+	 */
817
+	public function setOAuthRequestSecret($oAuthRequestSecret)
818
+	{
819
+		$this->oauthrequestsecret = $oAuthRequestSecret;
820
+	}
821
+
822
+	/**
823
+	 * Gets the user's access token
824
+	 * @category Security-Critical
825
+	 * @todo     move me to a collaborator
826
+	 * @return null|string
827
+	 */
828
+	public function getOAuthAccessToken()
829
+	{
830
+		return $this->oauthaccesstoken;
831
+	}
832
+
833
+	/**
834
+	 * Sets the user's access token
835
+	 * @todo move me to a collaborator
836
+	 *
837
+	 * @param string $oAuthAccessToken
838
+	 */
839
+	public function setOAuthAccessToken($oAuthAccessToken)
840
+	{
841
+		$this->oauthaccesstoken = $oAuthAccessToken;
842
+	}
843
+
844
+	/**
845
+	 * Gets the user's OAuth access secret
846
+	 * @category Security-Critical
847
+	 * @todo     move me to a collaborator
848
+	 * @return null|string
849
+	 */
850
+	public function getOAuthAccessSecret()
851
+	{
852
+		return $this->oauthaccesssecret;
853
+	}
854
+
855
+	/**
856
+	 * Sets the user's OAuth access secret
857
+	 * @todo move me to a collaborator
858
+	 *
859
+	 * @param string $oAuthAccessSecret
860
+	 */
861
+	public function setOAuthAccessSecret($oAuthAccessSecret)
862
+	{
863
+		$this->oauthaccesssecret = $oAuthAccessSecret;
864
+	}
865
+
866
+	#endregion
867
+
868
+	#region user access checks
869
+
870
+	/**
871
+	 * Tests if the user is an admin
872
+	 * @return bool
873
+	 * @category Security-Critical
874
+	 */
875
+	public function isAdmin()
876
+	{
877
+		return $this->status == self::STATUS_ADMIN;
878
+	}
879
+
880
+	/**
881
+	 * Tests if the user is a checkuser
882
+	 * @return bool
883
+	 * @category Security-Critical
884
+	 */
885
+	public function isCheckuser()
886
+	{
887
+		return $this->checkuser == 1 || $this->oauthCanCheckUser();
888
+	}
889
+
890
+	/**
891
+	 * Tests if the user is identified
892
+	 *
893
+	 * @param IdentificationVerifier $iv
894
+	 *
895
+	 * @return bool
896
+	 * @todo     Figure out what on earth is going on with PDO's typecasting here.  Apparently, it returns string("0") for
897
+	 *       the force-unidentified case, and int(1) for the identified case?!  This is quite ugly, but probably needed
898
+	 *       to play it safe for now.
899
+	 * @category Security-Critical
900
+	 */
901
+	public function isIdentified(IdentificationVerifier $iv)
902
+	{
903
+		if ($this->forceidentified === 0 || $this->forceidentified === "0") {
904
+			// User forced to unidentified in the database.
905
+			return false;
906
+		}
907
+		elseif ($this->forceidentified === 1 || $this->forceidentified === "1") {
908
+			// User forced to identified in the database.
909
+			return true;
910
+		}
911
+		else {
912
+			// User not forced to any particular identified status; consult IdentificationVerifier
913
+			return $iv->isUserIdentified($this->getOnWikiName());
914
+		}
915
+	}
916
+
917
+	/**
918
+	 * Tests if the user is suspended
919
+	 * @return bool
920
+	 * @category Security-Critical
921
+	 */
922
+	public function isSuspended()
923
+	{
924
+		return $this->status == self::STATUS_SUSPENDED;
925
+	}
926
+
927
+	/**
928
+	 * Tests if the user is new
929
+	 * @return bool
930
+	 * @category Security-Critical
931
+	 */
932
+	public function isNewUser()
933
+	{
934
+		return $this->status == self::STATUS_NEW;
935
+	}
936
+
937
+	/**
938
+	 * Tests if the user is a standard-level user
939
+	 * @return bool
940
+	 * @category Security-Critical
941
+	 */
942
+	public function isUser()
943
+	{
944
+		return $this->status == self::STATUS_USER;
945
+	}
946
+
947
+	/**
948
+	 * Tests if the user has been declined access to the tool
949
+	 * @return bool
950
+	 * @category Security-Critical
951
+	 */
952
+	public function isDeclined()
953
+	{
954
+		return $this->status == self::STATUS_DECLINED;
955
+	}
956
+
957
+	/**
958
+	 * Tests if the user is the community user
959
+	 *
960
+	 * @todo     decide if this means logged out. I think it usually does.
961
+	 * @return bool
962
+	 * @category Security-Critical
963
+	 */
964
+	public function isCommunityUser()
965
+	{
966
+		return false;
967
+	}
968
+
969
+	#endregion 
970
+
971
+	#region OAuth
972
+
973
+	/**
974
+	 * @todo     move me to a collaborator
975
+	 *
976
+	 * @param bool $useCached
977
+	 *
978
+	 * @return mixed|null
979
+	 * @category Security-Critical
980
+	 */
981
+	public function getOAuthIdentity($useCached = false)
982
+	{
983
+		if ($this->oauthaccesstoken === null) {
984
+			$this->clearOAuthData();
985
+		}
986
+
987
+		global $oauthConsumerToken, $oauthMediaWikiCanonicalServer;
988
+
989
+		if ($this->oauthidentitycache == null) {
990
+			$this->identityCache = null;
991
+		}
992
+		else {
993
+			$this->identityCache = unserialize($this->oauthidentitycache);
994
+		}
995
+
996
+		// check the cache
997
+		if (
998
+			$this->identityCache != null &&
999
+			$this->identityCache->aud == $oauthConsumerToken &&
1000
+			$this->identityCache->iss == $oauthMediaWikiCanonicalServer
1001
+		) {
1002
+			if (
1003
+				$useCached || (
1004
+					DateTime::createFromFormat("U", $this->identityCache->iat) < new DateTime() &&
1005
+					DateTime::createFromFormat("U", $this->identityCache->exp) > new DateTime()
1006
+				)
1007
+			) {
1008
+				// Use cached value - it's either valid or we don't care.
1009
+				return $this->identityCache;
1010
+			}
1011
+			else {
1012
+				// Cache expired and not forcing use of cached value
1013
+				$this->getIdentityCache();
1014
+
1015
+				return $this->identityCache;
1016
+			}
1017
+		}
1018
+		else {
1019
+			// Cache isn't ours or doesn't exist
1020
+			$this->getIdentityCache();
1021
+
1022
+			return $this->identityCache;
1023
+		}
1024
+	}
1025
+
1026
+	/**
1027
+	 * @todo     move me to a collaborator
1028
+	 *
1029
+	 * @param mixed $useCached Set to false for everything where up-to-date data is important.
1030
+	 *
1031
+	 * @return mixed
1032
+	 * @category Security-Critical
1033
+	 */
1034
+	private function getOAuthOnWikiName($useCached = false)
1035
+	{
1036
+		$identity = $this->getOAuthIdentity($useCached);
1037
+		if ($identity !== null) {
1038
+			return $identity->username;
1039
+		}
1040
+
1041
+		return false;
1042
+	}
1043
+
1044
+	/**
1045
+	 * @return bool
1046
+	 * @todo move me to a collaborator
1047
+	 */
1048
+	public function isOAuthLinked()
1049
+	{
1050
+		if ($this->onwikiname === "##OAUTH##") {
1051
+			return true; // special value. If an account must be oauth linked, this is true.
1052
+		}
1053
+
1054
+		return $this->oauthaccesstoken !== null;
1055
+	}
1056
+
1057
+	/**
1058
+	 * @return null
1059
+	 * @todo move me to a collaborator
1060
+	 */
1061
+	public function clearOAuthData()
1062
+	{
1063
+		$this->identityCache = null;
1064
+		$this->oauthidentitycache = null;
1065
+		$clearCacheQuery = "UPDATE user SET oauthidentitycache = NULL WHERE id = :id;";
1066
+		$this->dbObject->prepare($clearCacheQuery)->execute(array(":id" => $this->id));
1067
+
1068
+		return null;
1069
+	}
1070
+
1071
+	/**
1072
+	 * @throws Exception
1073
+	 * @todo     move me to a collaborator
1074
+	 * @category Security-Critical
1075
+	 */
1076
+	private function getIdentityCache()
1077
+	{
1078
+		/** @var IOAuthHelper $oauthHelper */
1079
+		global $oauthHelper;
1080
+
1081
+		try {
1082
+			$this->identityCache = $oauthHelper->getIdentityTicket($this->oauthaccesstoken, $this->oauthaccesssecret);
1083
+
1084
+			$this->oauthidentitycache = serialize($this->identityCache);
1085
+			$this->dbObject->prepare("UPDATE user SET oauthidentitycache = :identity WHERE id = :id;")
1086
+				->execute(array(":id" => $this->id, ":identity" => $this->oauthidentitycache));
1087
+		}
1088
+		catch (UnexpectedValueException $ex) {
1089
+			$this->identityCache = null;
1090
+			$this->oauthidentitycache = null;
1091
+			$this->dbObject->prepare("UPDATE user SET oauthidentitycache = NULL WHERE id = :id;")
1092
+				->execute(array(":id" => $this->id));
1093
+
1094
+			SessionAlert::warning("OAuth error getting identity from MediaWiki: " . $ex->getMessage());
1095
+		}
1096
+	}
1097
+
1098
+	/**
1099
+	 * @return bool
1100
+	 * @todo move me to a collaborator
1101
+	 */
1102
+	public function oauthCanUse()
1103
+	{
1104
+		try {
1105
+			return in_array('useoauth', $this->getOAuthIdentity()->grants);
1106
+		}
1107
+		catch (Exception $ex) {
1108
+			return false;
1109
+		}
1110
+	}
1111
+
1112
+	/**
1113
+	 * @return bool
1114
+	 * @todo move me to a collaborator
1115
+	 */
1116
+	public function oauthCanEdit()
1117
+	{
1118
+		try {
1119
+			return in_array('useoauth', $this->getOAuthIdentity()->grants)
1120
+			&& in_array('createeditmovepage', $this->getOAuthIdentity()->grants)
1121
+			&& in_array('createtalk', $this->getOAuthIdentity()->rights)
1122
+			&& in_array('edit', $this->getOAuthIdentity()->rights)
1123
+			&& in_array('writeapi', $this->getOAuthIdentity()->rights);
1124
+		}
1125
+		catch (Exception $ex) {
1126
+			return false;
1127
+		}
1128
+	}
1129
+
1130
+	/**
1131
+	 * @return bool
1132
+	 * @todo move me to a collaborator
1133
+	 */
1134
+	public function oauthCanCreateAccount()
1135
+	{
1136
+		try {
1137
+			return in_array('useoauth', $this->getOAuthIdentity()->grants)
1138
+			&& in_array('createaccount', $this->getOAuthIdentity()->grants)
1139
+			&& in_array('createaccount', $this->getOAuthIdentity()->rights)
1140
+			&& in_array('writeapi', $this->getOAuthIdentity()->rights);
1141
+		}
1142
+		catch (Exception $ex) {
1143
+			return false;
1144
+		}
1145
+	}
1146
+
1147
+	/**
1148
+	 * @return bool
1149
+	 * @todo     move me to a collaborator
1150
+	 * @category Security-Critical
1151
+	 */
1152
+	protected function oauthCanCheckUser()
1153
+	{
1154
+		if (!$this->isOAuthLinked()) {
1155
+			return false;
1156
+		}
1157
+
1158
+		try {
1159
+			$identity = $this->getOAuthIdentity();
1160
+
1161
+			return in_array('checkuser', $identity->rights);
1162
+		}
1163
+		catch (Exception $ex) {
1164
+			return false;
1165
+		}
1166
+	}
1167
+
1168
+	#endregion
1169
+
1170
+	/**
1171
+	 * Gets a hash of data for the user to reset their password with.
1172
+	 * @category Security-Critical
1173
+	 * @return string
1174
+	 */
1175
+	public function getForgottenPasswordHash()
1176
+	{
1177
+		return md5($this->username . $this->email . $this->welcome_template . $this->id . $this->password);
1178
+	}
1179
+
1180
+	/**
1181
+	 * Gets the approval date of the user
1182
+	 * @return DateTime|false
1183
+	 */
1184
+	public function getApprovalDate()
1185
+	{
1186
+		$query = $this->dbObject->prepare(<<<SQL
1187 1187
 			SELECT timestamp 
1188 1188
 			FROM log 
1189 1189
 			WHERE objectid = :userid
@@ -1192,12 +1192,12 @@  discard block
 block discarded – undo
1192 1192
 			ORDER BY id DESC 
1193 1193
 			LIMIT 1;
1194 1194
 SQL
1195
-        );
1196
-        $query->execute(array(":userid" => $this->id));
1195
+		);
1196
+		$query->execute(array(":userid" => $this->id));
1197 1197
 
1198
-        $data = DateTime::createFromFormat("Y-m-d H:i:s", $query->fetchColumn());
1199
-        $query->closeCursor();
1198
+		$data = DateTime::createFromFormat("Y-m-d H:i:s", $query->fetchColumn());
1199
+		$query->closeCursor();
1200 1200
 
1201
-        return $data;
1202
-    }
1201
+		return $data;
1202
+	}
1203 1203
 }
Please login to merge, or discard this patch.
includes/Fragments/TemplateOutput.php 1 patch
Indentation   +61 added lines, -61 removed lines patch added patch discarded remove patch
@@ -15,75 +15,75 @@
 block discarded – undo
15 15
 
16 16
 trait TemplateOutput
17 17
 {
18
-    /** @var Smarty */
19
-    private $smarty;
20
-    /** @var string Extra JavaScript to include at the end of the page's execution */
21
-    private $tailScript;
18
+	/** @var Smarty */
19
+	private $smarty;
20
+	/** @var string Extra JavaScript to include at the end of the page's execution */
21
+	private $tailScript;
22 22
 
23
-    /**
24
-     * @return SiteConfiguration
25
-     */
26
-    protected abstract function getSiteConfiguration();
23
+	/**
24
+	 * @return SiteConfiguration
25
+	 */
26
+	protected abstract function getSiteConfiguration();
27 27
 
28
-    /**
29
-     * Include extra JavaScript at the end of the page's execution
30
-     *
31
-     * @param $script string JavaScript to include at the end of the page
32
-     */
33
-    final protected function setTailScript($script)
34
-    {
35
-        $this->tailScript = $script;
36
-    }
28
+	/**
29
+	 * Include extra JavaScript at the end of the page's execution
30
+	 *
31
+	 * @param $script string JavaScript to include at the end of the page
32
+	 */
33
+	final protected function setTailScript($script)
34
+	{
35
+		$this->tailScript = $script;
36
+	}
37 37
 
38
-    /**
39
-     * Assigns a Smarty variable
40
-     *
41
-     * @param  array|string $name  the template variable name(s)
42
-     * @param  mixed        $value the value to assign
43
-     */
44
-    final protected function assign($name, $value)
45
-    {
46
-        $this->smarty->assign($name, $value);
47
-    }
38
+	/**
39
+	 * Assigns a Smarty variable
40
+	 *
41
+	 * @param  array|string $name  the template variable name(s)
42
+	 * @param  mixed        $value the value to assign
43
+	 */
44
+	final protected function assign($name, $value)
45
+	{
46
+		$this->smarty->assign($name, $value);
47
+	}
48 48
 
49
-    /**
50
-     * Sets up the variables used by the main Smarty base template.
51
-     *
52
-     * This list is getting kinda long.
53
-     */
54
-    final protected function setUpSmarty()
55
-    {
56
-        $this->smarty = new Smarty();
57
-        $this->smarty->addPluginsDir($this->getSiteConfiguration()->getFilePath() . '/smarty-plugins');
49
+	/**
50
+	 * Sets up the variables used by the main Smarty base template.
51
+	 *
52
+	 * This list is getting kinda long.
53
+	 */
54
+	final protected function setUpSmarty()
55
+	{
56
+		$this->smarty = new Smarty();
57
+		$this->smarty->addPluginsDir($this->getSiteConfiguration()->getFilePath() . '/smarty-plugins');
58 58
 
59
-        $this->assign('currentUser', User::getCommunity());
60
-        $this->assign('loggedIn', false);
61
-        $this->assign('baseurl', $this->getSiteConfiguration()->getBaseUrl());
62
-        $this->assign('mediawikiScriptPath', $this->getSiteConfiguration()->getMediawikiScriptPath());
59
+		$this->assign('currentUser', User::getCommunity());
60
+		$this->assign('loggedIn', false);
61
+		$this->assign('baseurl', $this->getSiteConfiguration()->getBaseUrl());
62
+		$this->assign('mediawikiScriptPath', $this->getSiteConfiguration()->getMediawikiScriptPath());
63 63
 
64
-        $this->assign('siteNoticeText', '');
65
-        $this->assign('toolversion', Environment::getToolVersion());
64
+		$this->assign('siteNoticeText', '');
65
+		$this->assign('toolversion', Environment::getToolVersion());
66 66
 
67
-        // default these
68
-        $this->assign('onlineusers', array());
69
-        $this->assign('typeAheadBlock', '');
70
-        $this->assign('extraJs', array());
71
-        $this->assign('extraCss', array());
67
+		// default these
68
+		$this->assign('onlineusers', array());
69
+		$this->assign('typeAheadBlock', '');
70
+		$this->assign('extraJs', array());
71
+		$this->assign('extraCss', array());
72 72
 
73
-        $this->assign('page', $this);
74
-    }
73
+		$this->assign('page', $this);
74
+	}
75 75
 
76
-    /**
77
-     * Fetches a rendered Smarty template
78
-     *
79
-     * @param $template string Template file path, relative to /templates/
80
-     *
81
-     * @return string Templated HTML
82
-     */
83
-    final protected function fetchTemplate($template)
84
-    {
85
-        $this->assign("tailScript", $this->tailScript);
76
+	/**
77
+	 * Fetches a rendered Smarty template
78
+	 *
79
+	 * @param $template string Template file path, relative to /templates/
80
+	 *
81
+	 * @return string Templated HTML
82
+	 */
83
+	final protected function fetchTemplate($template)
84
+	{
85
+		$this->assign("tailScript", $this->tailScript);
86 86
 
87
-        return $this->smarty->fetch($template);
88
-    }
87
+		return $this->smarty->fetch($template);
88
+	}
89 89
 }
Please login to merge, or discard this patch.
includes/Pages/PageUserManagement.php 1 patch
Indentation   +508 added lines, -508 removed lines patch added patch discarded remove patch
@@ -22,512 +22,512 @@
 block discarded – undo
22 22
  */
23 23
 class PageUserManagement extends InternalPageBase
24 24
 {
25
-    /** @var string */
26
-    private $adminMailingList = '[email protected]';
27
-
28
-    /**
29
-     * Main function for this page, when no specific actions are called.
30
-     */
31
-    protected function main()
32
-    {
33
-        $this->setHtmlTitle('User Management');
34
-
35
-        $database = $this->getDatabase();
36
-
37
-        if (WebRequest::getBoolean("showAll")) {
38
-            $this->assign("showAll", true);
39
-
40
-            $this->assign("suspendedUsers", User::getAllWithStatus(User::STATUS_SUSPENDED, $database));
41
-            $this->assign("declinedUsers", User::getAllWithStatus(User::STATUS_DECLINED, $database));
42
-        }
43
-        else {
44
-            $this->assign("showAll", false);
45
-            $this->assign("suspendedUsers", array());
46
-            $this->assign("declinedUsers", array());
47
-        }
48
-
49
-        $this->assign("newUsers", User::getAllWithStatus(User::STATUS_NEW, $database));
50
-        $this->assign("normalUsers", User::getAllWithStatus(User::STATUS_USER, $database));
51
-        $this->assign("adminUsers", User::getAllWithStatus(User::STATUS_ADMIN, $database));
52
-        $this->assign("checkUsers", User::getAllCheckusers($database));
53
-
54
-        $this->getTypeAheadHelper()->defineTypeAheadSource('username-typeahead', function() use ($database) {
55
-            return User::getAllUsernames($database);
56
-        });
57
-
58
-        $this->setTemplate("usermanagement/main.tpl");
59
-    }
60
-
61
-    #region Access control
62
-
63
-    /**
64
-     * Action target for suspending users
65
-     *
66
-     * @throws ApplicationLogicException
67
-     */
68
-    protected function suspend()
69
-    {
70
-        $this->setHtmlTitle('User Management');
71
-
72
-        $database = $this->getDatabase();
73
-
74
-        $userId = WebRequest::getInt('user');
75
-
76
-        /** @var User $user */
77
-        $user = User::getById($userId, $database);
78
-
79
-        if ($user === false) {
80
-            throw new ApplicationLogicException('Sorry, the user you are trying to suspend could not be found.');
81
-        }
82
-
83
-        if ($user->isSuspended()) {
84
-            throw new ApplicationLogicException('Sorry, the user you are trying to suspend is already suspended.');
85
-        }
86
-
87
-        // Dual-mode action
88
-        if (WebRequest::wasPosted()) {
89
-            $this->validateCSRFToken();
90
-            $reason = WebRequest::postString('reason');
91
-
92
-            if ($reason === null || trim($reason) === "") {
93
-                throw new ApplicationLogicException('No reason provided');
94
-            }
95
-
96
-            $user->setStatus(User::STATUS_SUSPENDED);
97
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
98
-            $user->save();
99
-            Logger::suspendedUser($database, $user, $reason);
100
-
101
-            $this->getNotificationHelper()->userSuspended($user, $reason);
102
-            SessionAlert::quick('Suspended user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
103
-
104
-            // send email
105
-            $this->sendStatusChangeEmail(
106
-                'Your WP:ACC account has been suspended',
107
-                'usermanagement/emails/suspended.tpl',
108
-                $reason,
109
-                $user,
110
-                User::getCurrent($database)->getUsername()
111
-            );
112
-
113
-            $this->redirect('userManagement');
114
-
115
-            return;
116
-        }
117
-        else {
118
-            $this->assignCSRFToken();
119
-            $this->setTemplate('usermanagement/changelevel-reason.tpl');
120
-            $this->assign('user', $user);
121
-            $this->assign('status', 'Suspended');
122
-            $this->assign("showReason", true);
123
-        }
124
-    }
125
-
126
-    /**
127
-     * Entry point for the decline action
128
-     *
129
-     * @throws ApplicationLogicException
130
-     */
131
-    protected function decline()
132
-    {
133
-        $this->setHtmlTitle('User Management');
134
-
135
-        $database = $this->getDatabase();
136
-
137
-        $userId = WebRequest::getInt('user');
138
-        $user = User::getById($userId, $database);
139
-
140
-        if ($user === false) {
141
-            throw new ApplicationLogicException('Sorry, the user you are trying to decline could not be found.');
142
-        }
143
-
144
-        if (!$user->isNewUser()) {
145
-            throw new ApplicationLogicException('Sorry, the user you are trying to decline is not new.');
146
-        }
147
-
148
-        // Dual-mode action
149
-        if (WebRequest::wasPosted()) {
150
-            $this->validateCSRFToken();
151
-            $reason = WebRequest::postString('reason');
152
-
153
-            if ($reason === null || trim($reason) === "") {
154
-                throw new ApplicationLogicException('No reason provided');
155
-            }
156
-
157
-            $user->setStatus(User::STATUS_DECLINED);
158
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
159
-            $user->save();
160
-            Logger::declinedUser($database, $user, $reason);
161
-
162
-            $this->getNotificationHelper()->userDeclined($user, $reason);
163
-            SessionAlert::quick('Declined user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
164
-
165
-            // send email
166
-            $this->sendStatusChangeEmail(
167
-                'Your WP:ACC account has been declined',
168
-                'usermanagement/emails/declined.tpl',
169
-                $reason,
170
-                $user,
171
-                User::getCurrent($database)->getUsername()
172
-            );
173
-
174
-            $this->redirect('userManagement');
175
-
176
-            return;
177
-        }
178
-        else {
179
-            $this->assignCSRFToken();
180
-            $this->setTemplate('usermanagement/changelevel-reason.tpl');
181
-            $this->assign('user', $user);
182
-            $this->assign('status', 'Declined');
183
-            $this->assign("showReason", true);
184
-        }
185
-    }
186
-
187
-    /**
188
-     * Entry point for the demote action
189
-     *
190
-     * @throws ApplicationLogicException
191
-     */
192
-    protected function demote()
193
-    {
194
-        $this->setHtmlTitle('User Management');
195
-
196
-        $database = $this->getDatabase();
197
-
198
-        $userId = WebRequest::getInt('user');
199
-        $user = User::getById($userId, $database);
200
-
201
-        if ($user === false) {
202
-            throw new ApplicationLogicException('Sorry, the user you are trying to demote could not be found.');
203
-        }
204
-
205
-        if (!$user->isAdmin()) {
206
-            throw new ApplicationLogicException('Sorry, the user you are trying to demote is not an admin.');
207
-        }
208
-
209
-        // Dual-mode action
210
-        if (WebRequest::wasPosted()) {
211
-            $this->validateCSRFToken();
212
-            $reason = WebRequest::postString('reason');
213
-
214
-            if ($reason === null || trim($reason) === "") {
215
-                throw new ApplicationLogicException('No reason provided');
216
-            }
217
-
218
-            $user->setStatus(User::STATUS_USER);
219
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
220
-            $user->save();
221
-            Logger::demotedUser($database, $user, $reason);
222
-
223
-            $this->getNotificationHelper()->userDemoted($user, $reason);
224
-            SessionAlert::quick('Demoted user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
225
-
226
-            // send email
227
-            $this->sendStatusChangeEmail(
228
-                'Your WP:ACC account has been demoted',
229
-                'usermanagement/emails/demoted.tpl',
230
-                $reason,
231
-                $user,
232
-                User::getCurrent($database)->getUsername()
233
-            );
234
-
235
-            $this->redirect('userManagement');
236
-
237
-            return;
238
-        }
239
-        else {
240
-            $this->assignCSRFToken();
241
-            $this->setTemplate('usermanagement/changelevel-reason.tpl');
242
-            $this->assign('user', $user);
243
-            $this->assign('status', 'User');
244
-            $this->assign("showReason", true);
245
-        }
246
-    }
247
-
248
-    /**
249
-     * Entry point for the approve action
250
-     *
251
-     * @throws ApplicationLogicException
252
-     */
253
-    protected function approve()
254
-    {
255
-        $this->setHtmlTitle('User Management');
256
-
257
-        $database = $this->getDatabase();
258
-
259
-        $userId = WebRequest::getInt('user');
260
-        $user = User::getById($userId, $database);
261
-
262
-        if ($user === false) {
263
-            throw new ApplicationLogicException('Sorry, the user you are trying to approve could not be found.');
264
-        }
265
-
266
-        if ($user->isUser() || $user->isAdmin()) {
267
-            throw new ApplicationLogicException('Sorry, the user you are trying to approve is already an active user.');
268
-        }
269
-
270
-        // Dual-mode action
271
-        if (WebRequest::wasPosted()) {
272
-            $this->validateCSRFToken();
273
-            $user->setStatus(User::STATUS_USER);
274
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
275
-            $user->save();
276
-            Logger::approvedUser($database, $user);
277
-
278
-            $this->getNotificationHelper()->userApproved($user);
279
-            SessionAlert::quick('Approved user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
280
-
281
-            // send email
282
-            $this->sendStatusChangeEmail(
283
-                'Your WP:ACC account has been approved',
284
-                'usermanagement/emails/approved.tpl',
285
-                null,
286
-                $user,
287
-                User::getCurrent($database)->getUsername()
288
-            );
289
-
290
-            $this->redirect("userManagement");
291
-
292
-            return;
293
-        }
294
-        else {
295
-            $this->assignCSRFToken();
296
-            $this->setTemplate("usermanagement/changelevel-reason.tpl");
297
-            $this->assign("user", $user);
298
-            $this->assign("status", "User");
299
-            $this->assign("showReason", false);
300
-        }
301
-    }
302
-
303
-    /**
304
-     * Entry point for the promote action
305
-     *
306
-     * @throws ApplicationLogicException
307
-     */
308
-    protected function promote()
309
-    {
310
-        $this->setHtmlTitle('User Management');
311
-
312
-        $database = $this->getDatabase();
313
-
314
-        $userId = WebRequest::getInt('user');
315
-        $user = User::getById($userId, $database);
316
-
317
-        if ($user === false) {
318
-            throw new ApplicationLogicException('Sorry, the user you are trying to promote could not be found.');
319
-        }
320
-
321
-        if ($user->isAdmin()) {
322
-            throw new ApplicationLogicException('Sorry, the user you are trying to promote is already an admin.');
323
-        }
324
-
325
-        // Dual-mode action
326
-        if (WebRequest::wasPosted()) {
327
-            $this->validateCSRFToken();
328
-            $user->setStatus(User::STATUS_ADMIN);
329
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
330
-            $user->save();
331
-            Logger::promotedUser($database, $user);
332
-
333
-            $this->getNotificationHelper()->userPromoted($user);
334
-            SessionAlert::quick('Promoted user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
335
-
336
-            // send email
337
-            $this->sendStatusChangeEmail(
338
-                'Your WP:ACC account has been promoted',
339
-                'usermanagement/emails/promoted.tpl',
340
-                null,
341
-                $user,
342
-                User::getCurrent($database)->getUsername()
343
-            );
344
-
345
-            $this->redirect("userManagement");
346
-
347
-            return;
348
-        }
349
-        else {
350
-            $this->assignCSRFToken();
351
-            $this->setTemplate("usermanagement/changelevel-reason.tpl");
352
-            $this->assign("user", $user);
353
-            $this->assign("status", "Admin");
354
-            $this->assign("showReason", false);
355
-        }
356
-    }
357
-
358
-    #endregion
359
-
360
-    #region Renaming / Editing
361
-
362
-    /**
363
-     * Entry point for the rename action
364
-     *
365
-     * @throws ApplicationLogicException
366
-     */
367
-    protected function rename()
368
-    {
369
-        $this->setHtmlTitle('User Management');
370
-
371
-        $database = $this->getDatabase();
372
-
373
-        $userId = WebRequest::getInt('user');
374
-        $user = User::getById($userId, $database);
375
-
376
-        if ($user === false) {
377
-            throw new ApplicationLogicException('Sorry, the user you are trying to rename could not be found.');
378
-        }
379
-
380
-        // Dual-mode action
381
-        if (WebRequest::wasPosted()) {
382
-            $this->validateCSRFToken();
383
-            $newUsername = WebRequest::postString('newname');
384
-
385
-            if ($newUsername === null || trim($newUsername) === "") {
386
-                throw new ApplicationLogicException('The new username cannot be empty');
387
-            }
388
-
389
-            if (User::getByUsername($newUsername, $database) != false) {
390
-                throw new ApplicationLogicException('The new username already exists');
391
-            }
392
-
393
-            $oldUsername = $user->getUsername();
394
-            $user->setUsername($newUsername);
395
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
396
-
397
-            $user->save();
398
-
399
-            $logEntryData = serialize(array(
400
-                'old' => $oldUsername,
401
-                'new' => $newUsername,
402
-            ));
403
-
404
-            Logger::renamedUser($database, $user, $logEntryData);
405
-
406
-            SessionAlert::quick("Changed User "
407
-                . htmlentities($oldUsername, ENT_COMPAT, 'UTF-8')
408
-                . " name to "
409
-                . htmlentities($newUsername, ENT_COMPAT, 'UTF-8'));
410
-
411
-            $this->getNotificationHelper()->userRenamed($user, $oldUsername);
412
-
413
-            // send an email to the user.
414
-            $this->assign('targetUsername', $user->getUsername());
415
-            $this->assign('toolAdmin', User::getCurrent($database)->getUsername());
416
-            $this->assign('oldUsername', $oldUsername);
417
-            $this->assign('mailingList', $this->adminMailingList);
418
-
419
-            $this->getEmailHelper()->sendMail(
420
-                $user->getEmail(),
421
-                'Your username on WP:ACC has been changed',
422
-                $this->fetchTemplate('usermanagement/emails/renamed.tpl'),
423
-                array('Reply-To' => $this->adminMailingList)
424
-            );
425
-
426
-            $this->redirect("userManagement");
427
-
428
-            return;
429
-        }
430
-        else {
431
-            $this->assignCSRFToken();
432
-            $this->setTemplate('usermanagement/renameuser.tpl');
433
-            $this->assign('user', $user);
434
-        }
435
-    }
436
-
437
-    /**
438
-     * Entry point for the edit action
439
-     *
440
-     * @throws ApplicationLogicException
441
-     */
442
-    protected function editUser()
443
-    {
444
-        $this->setHtmlTitle('User Management');
445
-
446
-        $database = $this->getDatabase();
447
-
448
-        $userId = WebRequest::getInt('user');
449
-        $user = User::getById($userId, $database);
450
-
451
-        if ($user === false) {
452
-            throw new ApplicationLogicException('Sorry, the user you are trying to edit could not be found.');
453
-        }
454
-
455
-        // Dual-mode action
456
-        if (WebRequest::wasPosted()) {
457
-            $this->validateCSRFToken();
458
-            $newEmail = WebRequest::postEmail('user_email');
459
-            $newOnWikiName = WebRequest::postString('user_onwikiname');
460
-
461
-            if ($newEmail === null) {
462
-                throw new ApplicationLogicException('Invalid email address');
463
-            }
464
-
465
-            if (!$user->isOAuthLinked()) {
466
-                if (trim($newOnWikiName) == "") {
467
-                    throw new ApplicationLogicException('New on-wiki username cannot be blank');
468
-                }
469
-
470
-                $user->setOnWikiName($newOnWikiName);
471
-            }
472
-
473
-            $user->setEmail($newEmail);
474
-
475
-            $user->setUpdateVersion(WebRequest::postInt('updateversion'));
476
-
477
-            $user->save();
478
-
479
-            Logger::userPreferencesChange($database, $user);
480
-            $this->getNotificationHelper()->userPrefChange($user);
481
-            SessionAlert::quick('Changes to user\'s preferences have been saved');
482
-
483
-            $this->redirect("userManagement");
484
-
485
-            return;
486
-        }
487
-        else {
488
-            $this->assignCSRFToken();
489
-            $this->setTemplate('usermanagement/edituser.tpl');
490
-            $this->assign('user', $user);
491
-        }
492
-    }
493
-
494
-    #endregion
495
-
496
-    /**
497
-     * Sets up the security for this page. If certain actions have different permissions, this should be reflected in
498
-     * the return value from this function.
499
-     *
500
-     * If this page even supports actions, you will need to check the route
501
-     *
502
-     * @return SecurityConfiguration
503
-     * @category Security-Critical
504
-     */
505
-    protected function getSecurityConfiguration()
506
-    {
507
-        return $this->getSecurityManager()->configure()->asAdminPage();
508
-    }
509
-
510
-    /**
511
-     * Sends a status change email to the user.
512
-     *
513
-     * @param string      $subject           The subject of the email
514
-     * @param string      $template          The smarty template to use
515
-     * @param string|null $reason            The reason for performing the status change
516
-     * @param User        $user              The user affected
517
-     * @param string      $toolAdminUsername The tool admin's username who is making the edit
518
-     */
519
-    private function sendStatusChangeEmail($subject, $template, $reason, $user, $toolAdminUsername)
520
-    {
521
-        $this->assign('targetUsername', $user->getUsername());
522
-        $this->assign('toolAdmin', $toolAdminUsername);
523
-        $this->assign('actionReason', $reason);
524
-        $this->assign('mailingList', $this->adminMailingList);
525
-
526
-        $this->getEmailHelper()->sendMail(
527
-            $user->getEmail(),
528
-            $subject,
529
-            $this->fetchTemplate($template),
530
-            array('Reply-To' => $this->adminMailingList)
531
-        );
532
-    }
25
+	/** @var string */
26
+	private $adminMailingList = '[email protected]';
27
+
28
+	/**
29
+	 * Main function for this page, when no specific actions are called.
30
+	 */
31
+	protected function main()
32
+	{
33
+		$this->setHtmlTitle('User Management');
34
+
35
+		$database = $this->getDatabase();
36
+
37
+		if (WebRequest::getBoolean("showAll")) {
38
+			$this->assign("showAll", true);
39
+
40
+			$this->assign("suspendedUsers", User::getAllWithStatus(User::STATUS_SUSPENDED, $database));
41
+			$this->assign("declinedUsers", User::getAllWithStatus(User::STATUS_DECLINED, $database));
42
+		}
43
+		else {
44
+			$this->assign("showAll", false);
45
+			$this->assign("suspendedUsers", array());
46
+			$this->assign("declinedUsers", array());
47
+		}
48
+
49
+		$this->assign("newUsers", User::getAllWithStatus(User::STATUS_NEW, $database));
50
+		$this->assign("normalUsers", User::getAllWithStatus(User::STATUS_USER, $database));
51
+		$this->assign("adminUsers", User::getAllWithStatus(User::STATUS_ADMIN, $database));
52
+		$this->assign("checkUsers", User::getAllCheckusers($database));
53
+
54
+		$this->getTypeAheadHelper()->defineTypeAheadSource('username-typeahead', function() use ($database) {
55
+			return User::getAllUsernames($database);
56
+		});
57
+
58
+		$this->setTemplate("usermanagement/main.tpl");
59
+	}
60
+
61
+	#region Access control
62
+
63
+	/**
64
+	 * Action target for suspending users
65
+	 *
66
+	 * @throws ApplicationLogicException
67
+	 */
68
+	protected function suspend()
69
+	{
70
+		$this->setHtmlTitle('User Management');
71
+
72
+		$database = $this->getDatabase();
73
+
74
+		$userId = WebRequest::getInt('user');
75
+
76
+		/** @var User $user */
77
+		$user = User::getById($userId, $database);
78
+
79
+		if ($user === false) {
80
+			throw new ApplicationLogicException('Sorry, the user you are trying to suspend could not be found.');
81
+		}
82
+
83
+		if ($user->isSuspended()) {
84
+			throw new ApplicationLogicException('Sorry, the user you are trying to suspend is already suspended.');
85
+		}
86
+
87
+		// Dual-mode action
88
+		if (WebRequest::wasPosted()) {
89
+			$this->validateCSRFToken();
90
+			$reason = WebRequest::postString('reason');
91
+
92
+			if ($reason === null || trim($reason) === "") {
93
+				throw new ApplicationLogicException('No reason provided');
94
+			}
95
+
96
+			$user->setStatus(User::STATUS_SUSPENDED);
97
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
98
+			$user->save();
99
+			Logger::suspendedUser($database, $user, $reason);
100
+
101
+			$this->getNotificationHelper()->userSuspended($user, $reason);
102
+			SessionAlert::quick('Suspended user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
103
+
104
+			// send email
105
+			$this->sendStatusChangeEmail(
106
+				'Your WP:ACC account has been suspended',
107
+				'usermanagement/emails/suspended.tpl',
108
+				$reason,
109
+				$user,
110
+				User::getCurrent($database)->getUsername()
111
+			);
112
+
113
+			$this->redirect('userManagement');
114
+
115
+			return;
116
+		}
117
+		else {
118
+			$this->assignCSRFToken();
119
+			$this->setTemplate('usermanagement/changelevel-reason.tpl');
120
+			$this->assign('user', $user);
121
+			$this->assign('status', 'Suspended');
122
+			$this->assign("showReason", true);
123
+		}
124
+	}
125
+
126
+	/**
127
+	 * Entry point for the decline action
128
+	 *
129
+	 * @throws ApplicationLogicException
130
+	 */
131
+	protected function decline()
132
+	{
133
+		$this->setHtmlTitle('User Management');
134
+
135
+		$database = $this->getDatabase();
136
+
137
+		$userId = WebRequest::getInt('user');
138
+		$user = User::getById($userId, $database);
139
+
140
+		if ($user === false) {
141
+			throw new ApplicationLogicException('Sorry, the user you are trying to decline could not be found.');
142
+		}
143
+
144
+		if (!$user->isNewUser()) {
145
+			throw new ApplicationLogicException('Sorry, the user you are trying to decline is not new.');
146
+		}
147
+
148
+		// Dual-mode action
149
+		if (WebRequest::wasPosted()) {
150
+			$this->validateCSRFToken();
151
+			$reason = WebRequest::postString('reason');
152
+
153
+			if ($reason === null || trim($reason) === "") {
154
+				throw new ApplicationLogicException('No reason provided');
155
+			}
156
+
157
+			$user->setStatus(User::STATUS_DECLINED);
158
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
159
+			$user->save();
160
+			Logger::declinedUser($database, $user, $reason);
161
+
162
+			$this->getNotificationHelper()->userDeclined($user, $reason);
163
+			SessionAlert::quick('Declined user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
164
+
165
+			// send email
166
+			$this->sendStatusChangeEmail(
167
+				'Your WP:ACC account has been declined',
168
+				'usermanagement/emails/declined.tpl',
169
+				$reason,
170
+				$user,
171
+				User::getCurrent($database)->getUsername()
172
+			);
173
+
174
+			$this->redirect('userManagement');
175
+
176
+			return;
177
+		}
178
+		else {
179
+			$this->assignCSRFToken();
180
+			$this->setTemplate('usermanagement/changelevel-reason.tpl');
181
+			$this->assign('user', $user);
182
+			$this->assign('status', 'Declined');
183
+			$this->assign("showReason", true);
184
+		}
185
+	}
186
+
187
+	/**
188
+	 * Entry point for the demote action
189
+	 *
190
+	 * @throws ApplicationLogicException
191
+	 */
192
+	protected function demote()
193
+	{
194
+		$this->setHtmlTitle('User Management');
195
+
196
+		$database = $this->getDatabase();
197
+
198
+		$userId = WebRequest::getInt('user');
199
+		$user = User::getById($userId, $database);
200
+
201
+		if ($user === false) {
202
+			throw new ApplicationLogicException('Sorry, the user you are trying to demote could not be found.');
203
+		}
204
+
205
+		if (!$user->isAdmin()) {
206
+			throw new ApplicationLogicException('Sorry, the user you are trying to demote is not an admin.');
207
+		}
208
+
209
+		// Dual-mode action
210
+		if (WebRequest::wasPosted()) {
211
+			$this->validateCSRFToken();
212
+			$reason = WebRequest::postString('reason');
213
+
214
+			if ($reason === null || trim($reason) === "") {
215
+				throw new ApplicationLogicException('No reason provided');
216
+			}
217
+
218
+			$user->setStatus(User::STATUS_USER);
219
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
220
+			$user->save();
221
+			Logger::demotedUser($database, $user, $reason);
222
+
223
+			$this->getNotificationHelper()->userDemoted($user, $reason);
224
+			SessionAlert::quick('Demoted user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
225
+
226
+			// send email
227
+			$this->sendStatusChangeEmail(
228
+				'Your WP:ACC account has been demoted',
229
+				'usermanagement/emails/demoted.tpl',
230
+				$reason,
231
+				$user,
232
+				User::getCurrent($database)->getUsername()
233
+			);
234
+
235
+			$this->redirect('userManagement');
236
+
237
+			return;
238
+		}
239
+		else {
240
+			$this->assignCSRFToken();
241
+			$this->setTemplate('usermanagement/changelevel-reason.tpl');
242
+			$this->assign('user', $user);
243
+			$this->assign('status', 'User');
244
+			$this->assign("showReason", true);
245
+		}
246
+	}
247
+
248
+	/**
249
+	 * Entry point for the approve action
250
+	 *
251
+	 * @throws ApplicationLogicException
252
+	 */
253
+	protected function approve()
254
+	{
255
+		$this->setHtmlTitle('User Management');
256
+
257
+		$database = $this->getDatabase();
258
+
259
+		$userId = WebRequest::getInt('user');
260
+		$user = User::getById($userId, $database);
261
+
262
+		if ($user === false) {
263
+			throw new ApplicationLogicException('Sorry, the user you are trying to approve could not be found.');
264
+		}
265
+
266
+		if ($user->isUser() || $user->isAdmin()) {
267
+			throw new ApplicationLogicException('Sorry, the user you are trying to approve is already an active user.');
268
+		}
269
+
270
+		// Dual-mode action
271
+		if (WebRequest::wasPosted()) {
272
+			$this->validateCSRFToken();
273
+			$user->setStatus(User::STATUS_USER);
274
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
275
+			$user->save();
276
+			Logger::approvedUser($database, $user);
277
+
278
+			$this->getNotificationHelper()->userApproved($user);
279
+			SessionAlert::quick('Approved user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
280
+
281
+			// send email
282
+			$this->sendStatusChangeEmail(
283
+				'Your WP:ACC account has been approved',
284
+				'usermanagement/emails/approved.tpl',
285
+				null,
286
+				$user,
287
+				User::getCurrent($database)->getUsername()
288
+			);
289
+
290
+			$this->redirect("userManagement");
291
+
292
+			return;
293
+		}
294
+		else {
295
+			$this->assignCSRFToken();
296
+			$this->setTemplate("usermanagement/changelevel-reason.tpl");
297
+			$this->assign("user", $user);
298
+			$this->assign("status", "User");
299
+			$this->assign("showReason", false);
300
+		}
301
+	}
302
+
303
+	/**
304
+	 * Entry point for the promote action
305
+	 *
306
+	 * @throws ApplicationLogicException
307
+	 */
308
+	protected function promote()
309
+	{
310
+		$this->setHtmlTitle('User Management');
311
+
312
+		$database = $this->getDatabase();
313
+
314
+		$userId = WebRequest::getInt('user');
315
+		$user = User::getById($userId, $database);
316
+
317
+		if ($user === false) {
318
+			throw new ApplicationLogicException('Sorry, the user you are trying to promote could not be found.');
319
+		}
320
+
321
+		if ($user->isAdmin()) {
322
+			throw new ApplicationLogicException('Sorry, the user you are trying to promote is already an admin.');
323
+		}
324
+
325
+		// Dual-mode action
326
+		if (WebRequest::wasPosted()) {
327
+			$this->validateCSRFToken();
328
+			$user->setStatus(User::STATUS_ADMIN);
329
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
330
+			$user->save();
331
+			Logger::promotedUser($database, $user);
332
+
333
+			$this->getNotificationHelper()->userPromoted($user);
334
+			SessionAlert::quick('Promoted user ' . htmlentities($user->getUsername(), ENT_COMPAT, 'UTF-8'));
335
+
336
+			// send email
337
+			$this->sendStatusChangeEmail(
338
+				'Your WP:ACC account has been promoted',
339
+				'usermanagement/emails/promoted.tpl',
340
+				null,
341
+				$user,
342
+				User::getCurrent($database)->getUsername()
343
+			);
344
+
345
+			$this->redirect("userManagement");
346
+
347
+			return;
348
+		}
349
+		else {
350
+			$this->assignCSRFToken();
351
+			$this->setTemplate("usermanagement/changelevel-reason.tpl");
352
+			$this->assign("user", $user);
353
+			$this->assign("status", "Admin");
354
+			$this->assign("showReason", false);
355
+		}
356
+	}
357
+
358
+	#endregion
359
+
360
+	#region Renaming / Editing
361
+
362
+	/**
363
+	 * Entry point for the rename action
364
+	 *
365
+	 * @throws ApplicationLogicException
366
+	 */
367
+	protected function rename()
368
+	{
369
+		$this->setHtmlTitle('User Management');
370
+
371
+		$database = $this->getDatabase();
372
+
373
+		$userId = WebRequest::getInt('user');
374
+		$user = User::getById($userId, $database);
375
+
376
+		if ($user === false) {
377
+			throw new ApplicationLogicException('Sorry, the user you are trying to rename could not be found.');
378
+		}
379
+
380
+		// Dual-mode action
381
+		if (WebRequest::wasPosted()) {
382
+			$this->validateCSRFToken();
383
+			$newUsername = WebRequest::postString('newname');
384
+
385
+			if ($newUsername === null || trim($newUsername) === "") {
386
+				throw new ApplicationLogicException('The new username cannot be empty');
387
+			}
388
+
389
+			if (User::getByUsername($newUsername, $database) != false) {
390
+				throw new ApplicationLogicException('The new username already exists');
391
+			}
392
+
393
+			$oldUsername = $user->getUsername();
394
+			$user->setUsername($newUsername);
395
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
396
+
397
+			$user->save();
398
+
399
+			$logEntryData = serialize(array(
400
+				'old' => $oldUsername,
401
+				'new' => $newUsername,
402
+			));
403
+
404
+			Logger::renamedUser($database, $user, $logEntryData);
405
+
406
+			SessionAlert::quick("Changed User "
407
+				. htmlentities($oldUsername, ENT_COMPAT, 'UTF-8')
408
+				. " name to "
409
+				. htmlentities($newUsername, ENT_COMPAT, 'UTF-8'));
410
+
411
+			$this->getNotificationHelper()->userRenamed($user, $oldUsername);
412
+
413
+			// send an email to the user.
414
+			$this->assign('targetUsername', $user->getUsername());
415
+			$this->assign('toolAdmin', User::getCurrent($database)->getUsername());
416
+			$this->assign('oldUsername', $oldUsername);
417
+			$this->assign('mailingList', $this->adminMailingList);
418
+
419
+			$this->getEmailHelper()->sendMail(
420
+				$user->getEmail(),
421
+				'Your username on WP:ACC has been changed',
422
+				$this->fetchTemplate('usermanagement/emails/renamed.tpl'),
423
+				array('Reply-To' => $this->adminMailingList)
424
+			);
425
+
426
+			$this->redirect("userManagement");
427
+
428
+			return;
429
+		}
430
+		else {
431
+			$this->assignCSRFToken();
432
+			$this->setTemplate('usermanagement/renameuser.tpl');
433
+			$this->assign('user', $user);
434
+		}
435
+	}
436
+
437
+	/**
438
+	 * Entry point for the edit action
439
+	 *
440
+	 * @throws ApplicationLogicException
441
+	 */
442
+	protected function editUser()
443
+	{
444
+		$this->setHtmlTitle('User Management');
445
+
446
+		$database = $this->getDatabase();
447
+
448
+		$userId = WebRequest::getInt('user');
449
+		$user = User::getById($userId, $database);
450
+
451
+		if ($user === false) {
452
+			throw new ApplicationLogicException('Sorry, the user you are trying to edit could not be found.');
453
+		}
454
+
455
+		// Dual-mode action
456
+		if (WebRequest::wasPosted()) {
457
+			$this->validateCSRFToken();
458
+			$newEmail = WebRequest::postEmail('user_email');
459
+			$newOnWikiName = WebRequest::postString('user_onwikiname');
460
+
461
+			if ($newEmail === null) {
462
+				throw new ApplicationLogicException('Invalid email address');
463
+			}
464
+
465
+			if (!$user->isOAuthLinked()) {
466
+				if (trim($newOnWikiName) == "") {
467
+					throw new ApplicationLogicException('New on-wiki username cannot be blank');
468
+				}
469
+
470
+				$user->setOnWikiName($newOnWikiName);
471
+			}
472
+
473
+			$user->setEmail($newEmail);
474
+
475
+			$user->setUpdateVersion(WebRequest::postInt('updateversion'));
476
+
477
+			$user->save();
478
+
479
+			Logger::userPreferencesChange($database, $user);
480
+			$this->getNotificationHelper()->userPrefChange($user);
481
+			SessionAlert::quick('Changes to user\'s preferences have been saved');
482
+
483
+			$this->redirect("userManagement");
484
+
485
+			return;
486
+		}
487
+		else {
488
+			$this->assignCSRFToken();
489
+			$this->setTemplate('usermanagement/edituser.tpl');
490
+			$this->assign('user', $user);
491
+		}
492
+	}
493
+
494
+	#endregion
495
+
496
+	/**
497
+	 * Sets up the security for this page. If certain actions have different permissions, this should be reflected in
498
+	 * the return value from this function.
499
+	 *
500
+	 * If this page even supports actions, you will need to check the route
501
+	 *
502
+	 * @return SecurityConfiguration
503
+	 * @category Security-Critical
504
+	 */
505
+	protected function getSecurityConfiguration()
506
+	{
507
+		return $this->getSecurityManager()->configure()->asAdminPage();
508
+	}
509
+
510
+	/**
511
+	 * Sends a status change email to the user.
512
+	 *
513
+	 * @param string      $subject           The subject of the email
514
+	 * @param string      $template          The smarty template to use
515
+	 * @param string|null $reason            The reason for performing the status change
516
+	 * @param User        $user              The user affected
517
+	 * @param string      $toolAdminUsername The tool admin's username who is making the edit
518
+	 */
519
+	private function sendStatusChangeEmail($subject, $template, $reason, $user, $toolAdminUsername)
520
+	{
521
+		$this->assign('targetUsername', $user->getUsername());
522
+		$this->assign('toolAdmin', $toolAdminUsername);
523
+		$this->assign('actionReason', $reason);
524
+		$this->assign('mailingList', $this->adminMailingList);
525
+
526
+		$this->getEmailHelper()->sendMail(
527
+			$user->getEmail(),
528
+			$subject,
529
+			$this->fetchTemplate($template),
530
+			array('Reply-To' => $this->adminMailingList)
531
+		);
532
+	}
533 533
 }
534 534
\ No newline at end of file
Please login to merge, or discard this patch.
includes/IdentificationVerifier.php 1 patch
Indentation   +157 added lines, -157 removed lines patch added patch discarded remove patch
@@ -26,131 +26,131 @@  discard block
 block discarded – undo
26 26
  */
27 27
 class IdentificationVerifier
28 28
 {
29
-    /**
30
-     * This field is an array of parameters, in key => value format, that should be appended to the Meta Wikimedia
31
-     * Web Service Endpoint URL to query if a user is listed on the Identification Noticeboard.  Note that URL encoding
32
-     * of these values is *not* necessary; this is done automatically.
33
-     *
34
-     * @var string[]
35
-     * @category Security-Critical
36
-     */
37
-    private static $apiQueryParameters = array(
38
-        'action'   => 'query',
39
-        'format'   => 'json',
40
-        'prop'     => 'links',
41
-        'titles'   => 'Access to nonpublic information policy/Noticeboard',
42
-        // Username of the user to be checked, with User: prefix, goes here!  Set in isIdentifiedOnWiki()
43
-        'pltitles' => '',
44
-    );
45
-    /** @var HttpHelper */
46
-    private $httpHelper;
47
-    /** @var SiteConfiguration */
48
-    private $siteConfiguration;
49
-    /** @var PdoDatabase */
50
-    private $dbObject;
51
-
52
-    /**
53
-     * IdentificationVerifier constructor.
54
-     *
55
-     * @param HttpHelper        $httpHelper
56
-     * @param SiteConfiguration $siteConfiguration
57
-     * @param PdoDatabase       $dbObject
58
-     */
59
-    public function __construct(HttpHelper $httpHelper, SiteConfiguration $siteConfiguration, PdoDatabase $dbObject)
60
-    {
61
-        $this->httpHelper = $httpHelper;
62
-        $this->siteConfiguration = $siteConfiguration;
63
-        $this->dbObject = $dbObject;
64
-    }
65
-
66
-    /**
67
-     * Checks if the given user is identified to the Wikimedia Foundation.
68
-     *
69
-     * @param string $onWikiName The Wikipedia username of the user
70
-     *
71
-     * @return bool
72
-     * @category Security-Critical
73
-     */
74
-    public function isUserIdentified($onWikiName)
75
-    {
76
-        if ($this->checkIdentificationCache($onWikiName)) {
77
-            return true;
78
-        }
79
-        else {
80
-            if ($this->isIdentifiedOnWiki($onWikiName)) {
81
-                $this->cacheIdentificationStatus($onWikiName);
82
-
83
-                return true;
84
-            }
85
-            else {
86
-                return false;
87
-            }
88
-        }
89
-    }
90
-
91
-    /**
92
-     * Checks if the given user has a valid entry in the idcache table.
93
-     *
94
-     * @param string $onWikiName The Wikipedia username of the user
95
-     *
96
-     * @return bool
97
-     * @category Security-Critical
98
-     */
99
-    private function checkIdentificationCache($onWikiName)
100
-    {
101
-        $interval = $this->siteConfiguration->getIdentificationCacheExpiry();
102
-
103
-        $query = <<<SQL
29
+	/**
30
+	 * This field is an array of parameters, in key => value format, that should be appended to the Meta Wikimedia
31
+	 * Web Service Endpoint URL to query if a user is listed on the Identification Noticeboard.  Note that URL encoding
32
+	 * of these values is *not* necessary; this is done automatically.
33
+	 *
34
+	 * @var string[]
35
+	 * @category Security-Critical
36
+	 */
37
+	private static $apiQueryParameters = array(
38
+		'action'   => 'query',
39
+		'format'   => 'json',
40
+		'prop'     => 'links',
41
+		'titles'   => 'Access to nonpublic information policy/Noticeboard',
42
+		// Username of the user to be checked, with User: prefix, goes here!  Set in isIdentifiedOnWiki()
43
+		'pltitles' => '',
44
+	);
45
+	/** @var HttpHelper */
46
+	private $httpHelper;
47
+	/** @var SiteConfiguration */
48
+	private $siteConfiguration;
49
+	/** @var PdoDatabase */
50
+	private $dbObject;
51
+
52
+	/**
53
+	 * IdentificationVerifier constructor.
54
+	 *
55
+	 * @param HttpHelper        $httpHelper
56
+	 * @param SiteConfiguration $siteConfiguration
57
+	 * @param PdoDatabase       $dbObject
58
+	 */
59
+	public function __construct(HttpHelper $httpHelper, SiteConfiguration $siteConfiguration, PdoDatabase $dbObject)
60
+	{
61
+		$this->httpHelper = $httpHelper;
62
+		$this->siteConfiguration = $siteConfiguration;
63
+		$this->dbObject = $dbObject;
64
+	}
65
+
66
+	/**
67
+	 * Checks if the given user is identified to the Wikimedia Foundation.
68
+	 *
69
+	 * @param string $onWikiName The Wikipedia username of the user
70
+	 *
71
+	 * @return bool
72
+	 * @category Security-Critical
73
+	 */
74
+	public function isUserIdentified($onWikiName)
75
+	{
76
+		if ($this->checkIdentificationCache($onWikiName)) {
77
+			return true;
78
+		}
79
+		else {
80
+			if ($this->isIdentifiedOnWiki($onWikiName)) {
81
+				$this->cacheIdentificationStatus($onWikiName);
82
+
83
+				return true;
84
+			}
85
+			else {
86
+				return false;
87
+			}
88
+		}
89
+	}
90
+
91
+	/**
92
+	 * Checks if the given user has a valid entry in the idcache table.
93
+	 *
94
+	 * @param string $onWikiName The Wikipedia username of the user
95
+	 *
96
+	 * @return bool
97
+	 * @category Security-Critical
98
+	 */
99
+	private function checkIdentificationCache($onWikiName)
100
+	{
101
+		$interval = $this->siteConfiguration->getIdentificationCacheExpiry();
102
+
103
+		$query = <<<SQL
104 104
 			SELECT COUNT(`id`)
105 105
 			FROM `idcache`
106 106
 			WHERE `onwikiusername` = :onwikiname
107 107
 				AND DATE_ADD(`checktime`, INTERVAL {$interval}) >= NOW();
108 108
 SQL;
109
-        $stmt = $this->dbObject->prepare($query);
110
-        $stmt->bindValue(':onwikiname', $onWikiName, PDO::PARAM_STR);
111
-        $stmt->execute();
112
-
113
-        // Guaranteed by the query to only return a single row with a single column
114
-        $results = $stmt->fetch(PDO::FETCH_NUM);
115
-
116
-        // I don't expect this to ever be a value other than 0 or 1 since the `onwikiusername` column is declared as a
117
-        // unique key - but meh.
118
-        return $results[0] != 0;
119
-    }
120
-
121
-    /**
122
-     * Does pretty much exactly what it says on the label - this method will clear all expired idcache entries from the
123
-     * idcache table.  Meant to be called periodically by a maintenance script.
124
-     *
125
-     * @param SiteConfiguration $siteConfiguration
126
-     * @param PdoDatabase       $dbObject
127
-     *
128
-     * @return void
129
-     */
130
-    public static function clearExpiredCacheEntries(SiteConfiguration $siteConfiguration, PdoDatabase $dbObject)
131
-    {
132
-        $interval = $siteConfiguration->getIdentificationCacheExpiry();
133
-
134
-        $query = <<<SQL
109
+		$stmt = $this->dbObject->prepare($query);
110
+		$stmt->bindValue(':onwikiname', $onWikiName, PDO::PARAM_STR);
111
+		$stmt->execute();
112
+
113
+		// Guaranteed by the query to only return a single row with a single column
114
+		$results = $stmt->fetch(PDO::FETCH_NUM);
115
+
116
+		// I don't expect this to ever be a value other than 0 or 1 since the `onwikiusername` column is declared as a
117
+		// unique key - but meh.
118
+		return $results[0] != 0;
119
+	}
120
+
121
+	/**
122
+	 * Does pretty much exactly what it says on the label - this method will clear all expired idcache entries from the
123
+	 * idcache table.  Meant to be called periodically by a maintenance script.
124
+	 *
125
+	 * @param SiteConfiguration $siteConfiguration
126
+	 * @param PdoDatabase       $dbObject
127
+	 *
128
+	 * @return void
129
+	 */
130
+	public static function clearExpiredCacheEntries(SiteConfiguration $siteConfiguration, PdoDatabase $dbObject)
131
+	{
132
+		$interval = $siteConfiguration->getIdentificationCacheExpiry();
133
+
134
+		$query = <<<SQL
135 135
 			DELETE FROM `idcache`
136 136
 			WHERE DATE_ADD(`checktime`, INTERVAL {$interval}) < NOW();
137 137
 SQL;
138
-        $dbObject->prepare($query)->execute();
139
-    }
140
-
141
-    /**
142
-     * This method will add an entry to the idcache that the given Wikipedia user has been verified as identified.  This
143
-     * is so we don't have to hit the API every single time we check.  The cache entry is valid for as long as specified
144
-     * in the ACC configuration (validity enforced by checkIdentificationCache() and clearExpiredCacheEntries()).
145
-     *
146
-     * @param string $onWikiName The Wikipedia username of the user
147
-     *
148
-     * @return void
149
-     * @category Security-Critical
150
-     */
151
-    private function cacheIdentificationStatus($onWikiName)
152
-    {
153
-        $query = <<<SQL
138
+		$dbObject->prepare($query)->execute();
139
+	}
140
+
141
+	/**
142
+	 * This method will add an entry to the idcache that the given Wikipedia user has been verified as identified.  This
143
+	 * is so we don't have to hit the API every single time we check.  The cache entry is valid for as long as specified
144
+	 * in the ACC configuration (validity enforced by checkIdentificationCache() and clearExpiredCacheEntries()).
145
+	 *
146
+	 * @param string $onWikiName The Wikipedia username of the user
147
+	 *
148
+	 * @return void
149
+	 * @category Security-Critical
150
+	 */
151
+	private function cacheIdentificationStatus($onWikiName)
152
+	{
153
+		$query = <<<SQL
154 154
 			INSERT INTO `idcache`
155 155
 				(`onwikiusername`)
156 156
 			VALUES
@@ -159,44 +159,44 @@  discard block
 block discarded – undo
159 159
 				`onwikiusername` = VALUES(`onwikiusername`),
160 160
 				`checktime` = CURRENT_TIMESTAMP;
161 161
 SQL;
162
-        $stmt = $this->dbObject->prepare($query);
163
-        $stmt->bindValue(':onwikiname', $onWikiName, PDO::PARAM_STR);
164
-        $stmt->execute();
165
-    }
166
-
167
-    /**
168
-     * Queries the Wikimedia API to determine if the specified user is listed on the identification noticeboard.
169
-     *
170
-     * @param string $onWikiName The Wikipedia username of the user
171
-     *
172
-     * @return bool
173
-     * @throws EnvironmentException
174
-     * @category Security-Critical
175
-     */
176
-    private function isIdentifiedOnWiki($onWikiName)
177
-    {
178
-        $strings = new StringFunctions();
179
-
180
-        // First character of Wikipedia usernames is always capitalized.
181
-        $onWikiName = $strings->ucfirst($onWikiName);
182
-
183
-        $parameters = self::$apiQueryParameters;
184
-        $parameters['pltitles'] = "User:" . $onWikiName;
185
-
186
-        try {
187
-            $endpoint = $this->siteConfiguration->getMetaWikimediaWebServiceEndpoint();
188
-            $response = $this->httpHelper->get($endpoint, $parameters);
189
-            $response = json_decode($response, true);
190
-        } catch (CurlException $ex) {
191
-            // failed getting identification status, so throw a nicer error.
192
-            $m = 'Could not contact metawiki API to determine user\' identification status. '
193
-                . 'This is probably a transient error, so please try again.';
194
-
195
-            throw new EnvironmentException($m, 0, $ex);
196
-        }
197
-
198
-        $page = @array_pop($response['query']['pages']);
199
-
200
-        return @$page['links'][0]['title'] === "User:" . $onWikiName;
201
-    }
162
+		$stmt = $this->dbObject->prepare($query);
163
+		$stmt->bindValue(':onwikiname', $onWikiName, PDO::PARAM_STR);
164
+		$stmt->execute();
165
+	}
166
+
167
+	/**
168
+	 * Queries the Wikimedia API to determine if the specified user is listed on the identification noticeboard.
169
+	 *
170
+	 * @param string $onWikiName The Wikipedia username of the user
171
+	 *
172
+	 * @return bool
173
+	 * @throws EnvironmentException
174
+	 * @category Security-Critical
175
+	 */
176
+	private function isIdentifiedOnWiki($onWikiName)
177
+	{
178
+		$strings = new StringFunctions();
179
+
180
+		// First character of Wikipedia usernames is always capitalized.
181
+		$onWikiName = $strings->ucfirst($onWikiName);
182
+
183
+		$parameters = self::$apiQueryParameters;
184
+		$parameters['pltitles'] = "User:" . $onWikiName;
185
+
186
+		try {
187
+			$endpoint = $this->siteConfiguration->getMetaWikimediaWebServiceEndpoint();
188
+			$response = $this->httpHelper->get($endpoint, $parameters);
189
+			$response = json_decode($response, true);
190
+		} catch (CurlException $ex) {
191
+			// failed getting identification status, so throw a nicer error.
192
+			$m = 'Could not contact metawiki API to determine user\' identification status. '
193
+				. 'This is probably a transient error, so please try again.';
194
+
195
+			throw new EnvironmentException($m, 0, $ex);
196
+		}
197
+
198
+		$page = @array_pop($response['query']['pages']);
199
+
200
+		return @$page['links'][0]['title'] === "User:" . $onWikiName;
201
+	}
202 202
 }
Please login to merge, or discard this patch.