1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file contains only the UserRepository class. |
4
|
|
|
*/ |
5
|
|
|
|
6
|
|
|
namespace Xtools; |
7
|
|
|
|
8
|
|
|
use Exception; |
9
|
|
|
use Mediawiki\Api\SimpleRequest; |
10
|
|
|
use Symfony\Component\DependencyInjection\Container; |
11
|
|
|
use Symfony\Component\HttpFoundation\Session\Session; |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* This class provides data for the User class. |
15
|
|
|
* @codeCoverageIgnore |
16
|
|
|
*/ |
17
|
|
|
class UserRepository extends Repository |
18
|
|
|
{ |
19
|
|
|
/** |
20
|
|
|
* Convenience method to get a new User object. |
21
|
|
|
* @param string $username The username. |
22
|
|
|
* @param Container $container The DI container. |
23
|
|
|
* @return User |
24
|
|
|
*/ |
25
|
|
|
public static function getUser($username, Container $container) |
26
|
|
|
{ |
27
|
|
|
$user = new User($username); |
28
|
|
|
$userRepo = new UserRepository(); |
29
|
|
|
$userRepo->setContainer($container); |
30
|
|
|
$user->setRepository($userRepo); |
31
|
|
|
return $user; |
32
|
|
|
} |
33
|
|
|
|
34
|
|
|
/** |
35
|
|
|
* Get the user's ID. |
36
|
|
|
* @param string $databaseName The database to query. |
37
|
|
|
* @param string $username The username to find. |
38
|
|
|
* @return int |
39
|
|
|
*/ |
40
|
|
View Code Duplication |
public function getId($databaseName, $username) |
|
|
|
|
41
|
|
|
{ |
42
|
|
|
$cacheKey = $this->getCacheKey(func_get_args(), 'user_id'); |
43
|
|
|
if ($this->cache->hasItem($cacheKey)) { |
44
|
|
|
return $this->cache->getItem($cacheKey)->get(); |
45
|
|
|
} |
46
|
|
|
|
47
|
|
|
$userTable = $this->getTableName($databaseName, 'user'); |
48
|
|
|
$sql = "SELECT user_id FROM $userTable WHERE user_name = :username LIMIT 1"; |
49
|
|
|
$resultQuery = $this->getProjectsConnection()->prepare($sql); |
50
|
|
|
$resultQuery->bindParam('username', $username); |
51
|
|
|
$resultQuery->execute(); |
52
|
|
|
$userId = (int)$resultQuery->fetchColumn(); |
53
|
|
|
|
54
|
|
|
// Cache for 10 minutes and return. |
55
|
|
|
$this->setCache($cacheKey, $userId); |
56
|
|
|
return $userId; |
57
|
|
|
} |
58
|
|
|
|
59
|
|
|
/** |
60
|
|
|
* Get the user's registration date. |
61
|
|
|
* @param string $databaseName The database to query. |
62
|
|
|
* @param string $username The username to find. |
63
|
|
|
* @return string|null As returned by the database. |
64
|
|
|
*/ |
65
|
|
View Code Duplication |
public function getRegistrationDate($databaseName, $username) |
|
|
|
|
66
|
|
|
{ |
67
|
|
|
$cacheKey = $this->getCacheKey(func_get_args(), 'user_registration'); |
68
|
|
|
if ($this->cache->hasItem($cacheKey)) { |
69
|
|
|
return $this->cache->getItem($cacheKey)->get(); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
$userTable = $this->getTableName($databaseName, 'user'); |
73
|
|
|
$sql = "SELECT user_registration FROM $userTable WHERE user_name = :username LIMIT 1"; |
74
|
|
|
$resultQuery = $this->getProjectsConnection()->prepare($sql); |
75
|
|
|
$resultQuery->bindParam('username', $username); |
76
|
|
|
$resultQuery->execute(); |
77
|
|
|
$registrationDate = $resultQuery->fetchColumn(); |
78
|
|
|
|
79
|
|
|
// Cache and return. |
80
|
|
|
$this->setCache($cacheKey, $registrationDate); |
81
|
|
|
return $registrationDate; |
82
|
|
|
} |
83
|
|
|
|
84
|
|
|
/** |
85
|
|
|
* Get the user's (system) edit count. |
86
|
|
|
* @param string $databaseName The database to query. |
87
|
|
|
* @param string $username The username to find. |
88
|
|
|
* @return int|null As returned by the database. |
89
|
|
|
*/ |
90
|
|
View Code Duplication |
public function getEditCount($databaseName, $username) |
|
|
|
|
91
|
|
|
{ |
92
|
|
|
$userTable = $this->getTableName($databaseName, 'user'); |
93
|
|
|
$sql = "SELECT user_editcount FROM $userTable WHERE user_name = :username LIMIT 1"; |
94
|
|
|
$resultQuery = $this->getProjectsConnection()->prepare($sql); |
95
|
|
|
$resultQuery->bindParam('username', $username); |
96
|
|
|
$resultQuery->execute(); |
97
|
|
|
return $resultQuery->fetchColumn(); |
98
|
|
|
} |
99
|
|
|
|
100
|
|
|
/** |
101
|
|
|
* Get group names of the given user. |
102
|
|
|
* @param Project $project The project. |
103
|
|
|
* @param string $username The username. |
104
|
|
|
* @return string[] |
105
|
|
|
*/ |
106
|
|
|
public function getGroups(Project $project, $username) |
107
|
|
|
{ |
108
|
|
|
// Use md5 to ensure the key does not contain reserved characters. |
109
|
|
|
$cacheKey = $this->getCacheKey(func_get_args(), 'user_groups'); |
110
|
|
|
if ($this->cache->hasItem($cacheKey)) { |
111
|
|
|
return $this->cache->getItem($cacheKey)->get(); |
112
|
|
|
} |
113
|
|
|
|
114
|
|
|
$this->stopwatch->start($cacheKey, 'XTools'); |
115
|
|
|
$api = $this->getMediawikiApi($project); |
116
|
|
|
$params = [ |
117
|
|
|
'list' => 'users', |
118
|
|
|
'ususers' => $username, |
119
|
|
|
'usprop' => 'groups' |
120
|
|
|
]; |
121
|
|
|
$query = new SimpleRequest('query', $params); |
122
|
|
|
$result = []; |
123
|
|
|
$res = $api->getRequest($query); |
124
|
|
|
if (isset($res['batchcomplete']) && isset($res['query']['users'][0]['groups'])) { |
125
|
|
|
$result = $res['query']['users'][0]['groups']; |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
// Cache for 10 minutes, and return. |
129
|
|
|
$this->setCache($cacheKey, $result); |
130
|
|
|
$this->stopwatch->stop($cacheKey); |
131
|
|
|
|
132
|
|
|
return $result; |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Get a user's global group membership (starting at XTools' default project if none is |
137
|
|
|
* provided). This requires the CentralAuth extension to be installed. |
138
|
|
|
* @link https://www.mediawiki.org/wiki/Extension:CentralAuth |
139
|
|
|
* @param string $username The username. |
140
|
|
|
* @param Project $project The project to query. |
141
|
|
|
* @return string[] |
142
|
|
|
*/ |
143
|
|
|
public function getGlobalGroups($username, Project $project = null) |
144
|
|
|
{ |
145
|
|
|
// Get the default project if not provided. |
146
|
|
|
if (!$project instanceof Project) { |
147
|
|
|
$project = ProjectRepository::getDefaultProject($this->container); |
148
|
|
|
} |
149
|
|
|
|
150
|
|
|
// Create the API query. |
151
|
|
|
$api = $this->getMediawikiApi($project); |
152
|
|
|
$params = [ |
153
|
|
|
'meta' => 'globaluserinfo', |
154
|
|
|
'guiuser' => $username, |
155
|
|
|
'guiprop' => 'groups' |
156
|
|
|
]; |
157
|
|
|
$query = new SimpleRequest('query', $params); |
158
|
|
|
|
159
|
|
|
// Get the result. |
160
|
|
|
$res = $api->getRequest($query); |
161
|
|
|
$result = []; |
162
|
|
|
if (isset($res['batchcomplete']) && isset($res['query']['globaluserinfo']['groups'])) { |
163
|
|
|
$result = $res['query']['globaluserinfo']['groups']; |
164
|
|
|
} |
165
|
|
|
return $result; |
166
|
|
|
} |
167
|
|
|
|
168
|
|
|
/** |
169
|
|
|
* Search the ipblocks table to see if the user is currently blocked |
170
|
|
|
* and return the expiry if they are. |
171
|
|
|
* @param $databaseName The database to query. |
|
|
|
|
172
|
|
|
* @param $userid The ID of the user to search for. |
173
|
|
|
* @return bool|string Expiry of active block or false |
174
|
|
|
*/ |
175
|
|
View Code Duplication |
public function getBlockExpiry($databaseName, $userid) |
|
|
|
|
176
|
|
|
{ |
177
|
|
|
$ipblocksTable = $this->getTableName($databaseName, 'ipblocks'); |
178
|
|
|
$sql = "SELECT ipb_expiry |
179
|
|
|
FROM $ipblocksTable |
180
|
|
|
WHERE ipb_user = :userid |
181
|
|
|
LIMIT 1"; |
182
|
|
|
$resultQuery = $this->getProjectsConnection()->prepare($sql); |
183
|
|
|
$resultQuery->bindParam('userid', $userid); |
184
|
|
|
$resultQuery->execute(); |
185
|
|
|
return $resultQuery->fetchColumn(); |
186
|
|
|
} |
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* Count the number of pages created by a user. |
190
|
|
|
* @param Project $project |
191
|
|
|
* @param User $user |
192
|
|
|
* @param string|int $namespace Namespace ID or 'all'. |
193
|
|
|
* @param string $redirects One of 'noredirects', 'onlyredirects' or blank for both. |
194
|
|
|
* @param string $deleted One of 'both', 'live' and 'deleted'. |
195
|
|
|
* @return string[] Result of query, see below. Includes live and deleted pages. |
196
|
|
|
*/ |
197
|
|
|
public function countPagesCreated( |
198
|
|
|
Project $project, |
199
|
|
|
User $user, |
200
|
|
|
$namespace, |
201
|
|
|
$redirects, |
202
|
|
|
$deleted |
203
|
|
|
) { |
204
|
|
|
$cacheKey = $this->getCacheKey(func_get_args(), 'num_user_pages_created'); |
205
|
|
|
if ($this->cache->hasItem($cacheKey)) { |
206
|
|
|
return $this->cache->getItem($cacheKey)->get(); |
207
|
|
|
} |
208
|
|
|
|
209
|
|
|
$conditions = [ |
210
|
|
|
'paSelects' => '', |
211
|
|
|
'paSelectsArchive' => '', |
212
|
|
|
'paJoin' => '', |
213
|
|
|
'revPageGroupBy' => '', |
214
|
|
|
]; |
215
|
|
|
$conditions = array_merge( |
216
|
|
|
$conditions, |
217
|
|
|
$this->getNamespaceRedirectAndDeletedPagesConditions($namespace, $redirects), |
218
|
|
|
$this->getUserConditions($project, $user) |
219
|
|
|
); |
220
|
|
|
|
221
|
|
|
$sql = "SELECT namespace, |
222
|
|
|
COUNT(page_title) AS count, |
223
|
|
|
SUM(CASE WHEN type = 'arc' THEN 1 ELSE 0 END) AS deleted, |
224
|
|
|
SUM(page_is_redirect) AS redirects |
225
|
|
|
FROM (". |
226
|
|
|
$this->getPagesCreatedInnerSql($project, $conditions, $deleted)." |
227
|
|
|
) a ". |
228
|
|
|
"GROUP BY namespace"; |
229
|
|
|
|
230
|
|
|
$resultQuery = $this->getProjectsConnection()->prepare($sql); |
231
|
|
|
$resultQuery->execute(); |
232
|
|
|
$result = $resultQuery->fetchAll(); |
233
|
|
|
|
234
|
|
|
// Cache for 10 minutes, and return. |
235
|
|
|
$this->setCache($cacheKey, $result); |
236
|
|
|
|
237
|
|
|
return $result; |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
/** |
241
|
|
|
* Get pages created by a user. |
242
|
|
|
* @param Project $project |
243
|
|
|
* @param User $user |
244
|
|
|
* @param string|int $namespace Namespace ID or 'all'. |
245
|
|
|
* @param string $redirects One of 'noredirects', 'onlyredirects' or blank for both. |
246
|
|
|
* @param string $deleted One of 'both', 'live' or 'deleted'. |
247
|
|
|
* @param int|null $limit Number of results to return, or blank to return all. |
248
|
|
|
* @param int $offset Number of pages past the initial dataset. Used for pagination. |
249
|
|
|
* @return string[] Result of query, see below. Includes live and deleted pages. |
250
|
|
|
*/ |
251
|
|
|
public function getPagesCreated( |
252
|
|
|
Project $project, |
253
|
|
|
User $user, |
254
|
|
|
$namespace, |
255
|
|
|
$redirects, |
256
|
|
|
$deleted, |
257
|
|
|
$limit = 1000, |
258
|
|
|
$offset = 0 |
259
|
|
|
) { |
260
|
|
|
$cacheKey = $this->getCacheKey(func_get_args(), 'user_pages_created'); |
261
|
|
|
if ($this->cache->hasItem($cacheKey)) { |
262
|
|
|
return $this->cache->getItem($cacheKey)->get(); |
263
|
|
|
} |
264
|
|
|
$this->stopwatch->start($cacheKey, 'XTools'); |
265
|
|
|
|
266
|
|
|
$conditions = [ |
267
|
|
|
'paSelects' => '', |
268
|
|
|
'paSelectsArchive' => '', |
269
|
|
|
'paJoin' => '', |
270
|
|
|
'revPageGroupBy' => '', |
271
|
|
|
]; |
272
|
|
|
|
273
|
|
|
$conditions = array_merge( |
274
|
|
|
$conditions, |
275
|
|
|
$this->getNamespaceRedirectAndDeletedPagesConditions($namespace, $redirects), |
276
|
|
|
$this->getUserConditions($project, $user) |
277
|
|
|
); |
278
|
|
|
|
279
|
|
|
$pageAssessmentsTable = $project->getTableName('page_assessments'); |
280
|
|
|
|
281
|
|
|
$hasPageAssessments = $this->isLabs() && $project->hasPageAssessments(); |
282
|
|
|
if ($hasPageAssessments) { |
283
|
|
|
$conditions['paSelects'] = ', pa_class, pa_importance, pa_page_revision'; |
284
|
|
|
$conditions['paSelectsArchive'] = ', NULL AS pa_class, NULL AS pa_page_id, '. |
285
|
|
|
'NULL AS pa_page_revision'; |
286
|
|
|
$conditions['paJoin'] = "LEFT JOIN $pageAssessmentsTable ON rev_page = pa_page_id"; |
287
|
|
|
$conditions['revPageGroupBy'] = 'GROUP BY rev_page'; |
288
|
|
|
} |
289
|
|
|
|
290
|
|
|
$sql = "SELECT * FROM (". |
291
|
|
|
$this->getPagesCreatedInnerSql($project, $conditions, $deleted)." |
292
|
|
|
) a ". |
293
|
|
|
"ORDER BY rev_timestamp DESC |
294
|
|
|
".(!empty($limit) ? "LIMIT $limit OFFSET $offset" : ''); |
295
|
|
|
|
296
|
|
|
$resultQuery = $this->getProjectsConnection()->prepare($sql); |
297
|
|
|
$resultQuery->execute(); |
298
|
|
|
$result = $resultQuery->fetchAll(); |
299
|
|
|
|
300
|
|
|
// Cache for 10 minutes, and return. |
301
|
|
|
$this->setCache($cacheKey, $result); |
302
|
|
|
$this->stopwatch->stop($cacheKey); |
303
|
|
|
|
304
|
|
|
return $result; |
305
|
|
|
} |
306
|
|
|
|
307
|
|
|
/** |
308
|
|
|
* Get SQL fragments for the namespace and redirects, |
309
|
|
|
* to be used in self::getPagesCreatedInnerSql(). |
310
|
|
|
* @param string|int $namespace Namespace ID or 'all'. |
311
|
|
|
* @param string $redirects One of 'noredirects', 'onlyredirects' or blank for both. |
312
|
|
|
* @return string[] With keys 'namespaceRev', 'namespaceArc' and 'redirects' |
313
|
|
|
*/ |
314
|
|
|
private function getNamespaceRedirectAndDeletedPagesConditions($namespace, $redirects) |
315
|
|
|
{ |
316
|
|
|
$conditions = [ |
317
|
|
|
'namespaceArc' => '', |
318
|
|
|
'namespaceRev' => '', |
319
|
|
|
'redirects' => '' |
320
|
|
|
]; |
321
|
|
|
|
322
|
|
|
if ($namespace !== 'all') { |
323
|
|
|
$conditions['namespaceRev'] = " AND page_namespace = '".intval($namespace)."' "; |
324
|
|
|
$conditions['namespaceArc'] = " AND ar_namespace = '".intval($namespace)."' "; |
325
|
|
|
} |
326
|
|
|
|
327
|
|
|
if ($redirects == 'onlyredirects') { |
328
|
|
|
$conditions['redirects'] = " AND page_is_redirect = '1' "; |
329
|
|
|
} elseif ($redirects == 'noredirects') { |
330
|
|
|
$conditions['redirects'] = " AND page_is_redirect = '0' "; |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
return $conditions; |
334
|
|
|
} |
335
|
|
|
|
336
|
|
|
/** |
337
|
|
|
* Get SQL fragments for rev_user or rev_user_text, depending on if the user is logged out. |
338
|
|
|
* Used in self::getPagesCreatedInnerSql(). |
339
|
|
|
* @param Project $project |
340
|
|
|
* @param User $user |
341
|
|
|
* @return string[] Keys 'whereRev' and 'whereArc'. |
342
|
|
|
*/ |
343
|
|
|
private function getUserConditions(Project $project, User $user) |
344
|
|
|
{ |
345
|
|
|
$username = $user->getUsername(); |
346
|
|
|
$userId = $user->getId($project); |
347
|
|
|
|
348
|
|
|
if ($userId == 0) { // IP Editor or undefined username. |
349
|
|
|
return [ |
350
|
|
|
'whereRev' => " rev_user_text = '$username' AND rev_user = '0' ", |
351
|
|
|
'whereArc' => " ar_user_text = '$username' AND ar_user = '0' ", |
352
|
|
|
]; |
353
|
|
|
} else { |
354
|
|
|
return [ |
355
|
|
|
'whereRev' => " rev_user = '$userId' AND rev_timestamp > 1 ", |
356
|
|
|
'whereArc' => " ar_user = '$userId' AND ar_timestamp > 1 ", |
357
|
|
|
]; |
358
|
|
|
} |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* Inner SQL for getting or counting pages created by the user. |
363
|
|
|
* @param Project $project |
364
|
|
|
* @param string[] $conditions Conditions for the SQL, must include 'paSelects', |
365
|
|
|
* 'paSelectsArchive', 'paJoin', 'whereRev', 'whereArc', 'namespaceRev', 'namespaceArc', |
366
|
|
|
* 'redirects' and 'revPageGroupBy'. |
367
|
|
|
* @param string $deleted One of 'live', 'deleted' or 'both'. |
368
|
|
|
* @return string Raw SQL. |
369
|
|
|
*/ |
370
|
|
|
private function getPagesCreatedInnerSql(Project $project, $conditions, $deleted) |
371
|
|
|
{ |
372
|
|
|
$pageTable = $project->getTableName('page'); |
373
|
|
|
$revisionTable = $project->getTableName('revision'); |
374
|
|
|
$archiveTable = $project->getTableName('archive'); |
375
|
|
|
$logTable = $project->getTableName('logging', 'logindex'); |
376
|
|
|
|
377
|
|
|
$revisionsSelect = " |
378
|
|
|
SELECT DISTINCT page_namespace AS namespace, 'rev' AS type, page_title AS page_title, |
379
|
|
|
page_len, page_is_redirect, rev_timestamp AS rev_timestamp, |
380
|
|
|
rev_user, rev_user_text AS username, rev_len, rev_id ".$conditions['paSelects']." |
381
|
|
|
FROM $pageTable |
382
|
|
|
JOIN $revisionTable ON page_id = rev_page ". |
383
|
|
|
$conditions['paJoin']." |
384
|
|
|
WHERE ".$conditions['whereRev']." |
385
|
|
|
AND rev_parent_id = '0'". |
386
|
|
|
$conditions['namespaceRev']. |
387
|
|
|
$conditions['redirects']. |
388
|
|
|
$conditions['revPageGroupBy']; |
389
|
|
|
|
390
|
|
|
$archiveSelect = " |
391
|
|
|
SELECT ar_namespace AS namespace, 'arc' AS type, ar_title AS page_title, |
392
|
|
|
0 AS page_len, '0' AS page_is_redirect, MIN(ar_timestamp) AS rev_timestamp, |
393
|
|
|
ar_user AS rev_user, ar_user_text AS username, ar_len AS rev_len, |
394
|
|
|
ar_rev_id AS rev_id ".$conditions['paSelectsArchive']." |
395
|
|
|
FROM $archiveTable |
396
|
|
|
LEFT JOIN $logTable ON log_namespace = ar_namespace AND log_title = ar_title |
397
|
|
|
AND log_user = ar_user AND (log_action = 'move' OR log_action = 'move_redir') |
398
|
|
|
AND log_type = 'move' |
399
|
|
|
WHERE ".$conditions['whereArc']." |
400
|
|
|
AND ar_parent_id = '0' ". |
401
|
|
|
$conditions['namespaceArc']." |
402
|
|
|
AND log_action IS NULL |
403
|
|
|
GROUP BY ar_namespace, ar_title"; |
404
|
|
|
|
405
|
|
|
if ($deleted == 'live') { |
406
|
|
|
return $revisionsSelect; |
407
|
|
|
} elseif ($deleted == 'deleted') { |
408
|
|
|
return $archiveSelect; |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
return "($revisionsSelect) UNION ($archiveSelect)"; |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
/** |
415
|
|
|
* Get edit count within given timeframe and namespace. |
416
|
|
|
* @param Project $project |
417
|
|
|
* @param User $user |
418
|
|
|
* @param int|string $namespace Namespace ID or 'all' for all namespaces |
419
|
|
|
* @param string $start Start date in a format accepted by strtotime() |
420
|
|
|
* @param string $end End date in a format accepted by strtotime() |
421
|
|
|
*/ |
422
|
|
|
public function countEdits(Project $project, User $user, $namespace = 'all', $start = '', $end = '') |
423
|
|
|
{ |
424
|
|
|
$cacheKey = $this->getCacheKey(func_get_args(), 'user_editcount'); |
425
|
|
|
if ($this->cache->hasItem($cacheKey)) { |
426
|
|
|
return $this->cache->getItem($cacheKey)->get(); |
427
|
|
|
} |
428
|
|
|
|
429
|
|
|
list($condBegin, $condEnd) = $this->getRevTimestampConditions($start, $end); |
430
|
|
|
list($pageJoin, $condNamespace) = $this->getPageAndNamespaceSql($project, $namespace); |
431
|
|
|
$revisionTable = $project->getTableName('revision'); |
432
|
|
|
|
433
|
|
|
$sql = "SELECT COUNT(rev_id) |
434
|
|
|
FROM $revisionTable |
435
|
|
|
$pageJoin |
436
|
|
|
WHERE rev_user_text = :username |
437
|
|
|
$condNamespace |
438
|
|
|
$condBegin |
439
|
|
|
$condEnd"; |
440
|
|
|
|
441
|
|
|
$resultQuery = $this->executeQuery($sql, $user, $namespace, $start, $end); |
442
|
|
|
$result = $resultQuery->fetchColumn(); |
443
|
|
|
|
444
|
|
|
// Cache for 10 minutes, and return. |
445
|
|
|
$this->setCache($cacheKey, $result); |
446
|
|
|
|
447
|
|
|
return $result; |
448
|
|
|
} |
449
|
|
|
|
450
|
|
|
/** |
451
|
|
|
* Get information about the currently-logged in user. |
452
|
|
|
* @return array |
453
|
|
|
*/ |
454
|
|
|
public function getXtoolsUserInfo() |
455
|
|
|
{ |
456
|
|
|
/** @var Session $session */ |
457
|
|
|
$session = $this->container->get('session'); |
458
|
|
|
return $session->get('logged_in_user'); |
459
|
|
|
} |
460
|
|
|
|
461
|
|
|
/** |
462
|
|
|
* Maximum number of edits to process, based on configuration. |
463
|
|
|
* @return int |
464
|
|
|
*/ |
465
|
|
|
public function maxEdits() |
466
|
|
|
{ |
467
|
|
|
return $this->container->getParameter('app.max_user_edits'); |
468
|
|
|
} |
469
|
|
|
|
470
|
|
|
/** |
471
|
|
|
* Get SQL clauses for joining on `page` and restricting to a namespace. |
472
|
|
|
* @param Project $project |
473
|
|
|
* @param int|string $namespace Namespace ID or 'all' for all namespaces. |
474
|
|
|
* @return array [page join clause, page namespace clause] |
475
|
|
|
*/ |
476
|
|
|
protected function getPageAndNamespaceSql(Project $project, $namespace) |
477
|
|
|
{ |
478
|
|
|
if ($namespace === 'all') { |
479
|
|
|
return [null, null]; |
480
|
|
|
} |
481
|
|
|
|
482
|
|
|
$pageTable = $project->getTableName('page'); |
483
|
|
|
$pageJoin = $namespace !== 'all' ? "LEFT JOIN $pageTable ON rev_page = page_id" : null; |
484
|
|
|
$condNamespace = 'AND page_namespace = :namespace'; |
485
|
|
|
|
486
|
|
|
return [$pageJoin, $condNamespace]; |
487
|
|
|
} |
488
|
|
|
|
489
|
|
|
/** |
490
|
|
|
* Get SQL clauses for rev_timestamp, based on whether values for |
491
|
|
|
* the given start and end parameters exist. |
492
|
|
|
* @param string $start |
493
|
|
|
* @param string $end |
494
|
|
|
* @return string[] Clauses for start and end timestamps. |
495
|
|
|
*/ |
496
|
|
|
protected function getRevTimestampConditions($start, $end) |
497
|
|
|
{ |
498
|
|
|
$condBegin = ''; |
499
|
|
|
$condEnd = ''; |
500
|
|
|
|
501
|
|
|
if (!empty($start)) { |
502
|
|
|
$condBegin = 'AND rev_timestamp >= :start '; |
503
|
|
|
} |
504
|
|
|
if (!empty($end)) { |
505
|
|
|
$condEnd = 'AND rev_timestamp <= :end '; |
506
|
|
|
} |
507
|
|
|
|
508
|
|
|
return [$condBegin, $condEnd]; |
509
|
|
|
} |
510
|
|
|
|
511
|
|
|
/** |
512
|
|
|
* Prepare the given SQL, bind the given parameters, and execute the Doctrine Statement. |
513
|
|
|
* @param string $sql |
514
|
|
|
* @param User $user |
515
|
|
|
* @param string $namespace |
516
|
|
|
* @param string $start |
517
|
|
|
* @param string $end |
518
|
|
|
* @return \Doctrine\DBAL\Statement |
519
|
|
|
*/ |
520
|
|
|
protected function executeQuery($sql, User $user, $namespace = 'all', $start = '', $end = '') |
521
|
|
|
{ |
522
|
|
|
$resultQuery = $this->getProjectsConnection()->prepare($sql); |
523
|
|
|
$username = $user->getUsername(); |
524
|
|
|
$resultQuery->bindParam('username', $username); |
525
|
|
|
|
526
|
|
|
if (!empty($start)) { |
527
|
|
|
$start = date('Ymd000000', strtotime($start)); |
|
|
|
|
528
|
|
|
$resultQuery->bindParam('start', $start); |
529
|
|
|
} |
530
|
|
|
if (!empty($end)) { |
531
|
|
|
$end = date('Ymd235959', strtotime($end)); |
532
|
|
|
$resultQuery->bindParam('end', $end); |
533
|
|
|
} |
534
|
|
|
if ($namespace !== 'all') { |
535
|
|
|
$resultQuery->bindParam('namespace', $namespace); |
536
|
|
|
} |
537
|
|
|
|
538
|
|
|
$resultQuery->execute(); |
539
|
|
|
|
540
|
|
|
return $resultQuery; |
541
|
|
|
} |
542
|
|
|
} |
543
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.