1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* This file contains only the PagesController class. |
4
|
|
|
*/ |
5
|
|
|
|
6
|
|
|
namespace AppBundle\Controller; |
7
|
|
|
|
8
|
|
|
use DateTime; |
9
|
|
|
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route; |
10
|
|
|
use Symfony\Bundle\FrameworkBundle\Controller\Controller; |
11
|
|
|
use Symfony\Component\HttpFoundation\RedirectResponse; |
12
|
|
|
use Symfony\Component\HttpFoundation\Request; |
13
|
|
|
use Symfony\Component\HttpFoundation\Response; |
14
|
|
|
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
15
|
|
|
use Xtools\ProjectRepository; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* This controller serves the Pages tool. |
19
|
|
|
*/ |
20
|
|
|
class PagesController extends Controller |
21
|
|
|
{ |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Get the tool's shortname. |
25
|
|
|
* @return string |
26
|
|
|
*/ |
27
|
|
|
public function getToolShortname() |
28
|
|
|
{ |
29
|
|
|
return 'pages'; |
30
|
|
|
} |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Display the form. |
34
|
|
|
* @Route("/pages", name="pages") |
35
|
|
|
* @Route("/pages", name="Pages") |
36
|
|
|
* @Route("/pages/", name="PagesSlash") |
37
|
|
|
* @Route("/pages/index.php", name="PagesIndexPhp") |
38
|
|
|
* @Route("/pages/{project}", name="PagesProject") |
39
|
|
|
* @param string $project The project domain name. |
40
|
|
|
* @return Response |
41
|
|
|
*/ |
42
|
|
|
public function indexAction($project = null) |
43
|
|
|
{ |
44
|
|
|
// Grab the request object, grab the values out of it. |
45
|
|
|
$request = Request::createFromGlobals(); |
46
|
|
|
|
47
|
|
|
$projectQuery = $request->query->get('project'); |
48
|
|
|
$username = $request->query->get('username', $request->query->get('user')); |
49
|
|
|
$namespace = $request->query->get('namespace'); |
50
|
|
|
$redirects = $request->query->get('redirects'); |
51
|
|
|
|
52
|
|
|
// if values for required parameters are present, redirect to result action |
53
|
|
|
if ($projectQuery != "" && $username != "" && $namespace != "" && $redirects != "") { |
54
|
|
|
return $this->redirectToRoute("PagesResult", [ |
55
|
|
|
'project'=>$projectQuery, |
56
|
|
|
'username' => $username, |
57
|
|
|
'namespace'=>$namespace, |
58
|
|
|
'redirects'=>$redirects, |
59
|
|
|
]); |
60
|
|
View Code Duplication |
} elseif ($projectQuery != "" && $username != "" && $namespace != "") { |
|
|
|
|
61
|
|
|
return $this->redirectToRoute("PagesResult", [ |
62
|
|
|
'project'=>$projectQuery, |
63
|
|
|
'username' => $username, |
64
|
|
|
'namespace'=>$namespace, |
65
|
|
|
]); |
66
|
|
|
} elseif ($projectQuery != "" && $username != "" && $redirects != "") { |
67
|
|
|
return $this->redirectToRoute("PagesResult", [ |
68
|
|
|
'project'=>$projectQuery, |
69
|
|
|
'username' => $username, |
70
|
|
|
'redirects'=>$redirects, |
71
|
|
|
]); |
72
|
|
|
} elseif ($projectQuery != "" && $username != "") { |
73
|
|
|
return $this->redirectToRoute("PagesResult", [ |
74
|
|
|
'project'=>$projectQuery, |
75
|
|
|
'username' => $username, |
76
|
|
|
]); |
77
|
|
|
} elseif ($projectQuery != "") { |
78
|
|
|
return $this->redirectToRoute("PagesProject", [ 'project'=>$projectQuery ]); |
79
|
|
|
} |
80
|
|
|
|
81
|
|
|
// set default wiki so we can populate the namespace selector |
82
|
|
|
if (!$project) { |
|
|
|
|
83
|
|
|
$project = $this->getParameter('default_project'); |
84
|
|
|
} |
85
|
|
|
|
86
|
|
|
$projectData = ProjectRepository::getProject($project, $this->container); |
87
|
|
|
|
88
|
|
|
$namespaces = null; |
89
|
|
|
|
90
|
|
|
if ($projectData->exists()) { |
91
|
|
|
$namespaces = $projectData->getNamespaces(); |
92
|
|
|
} |
93
|
|
|
|
94
|
|
|
// Otherwise fall through. |
95
|
|
|
return $this->render('pages/index.html.twig', [ |
96
|
|
|
'xtPageTitle' => 'tool-pages', |
97
|
|
|
'xtSubtitle' => 'tool-pages-desc', |
98
|
|
|
'xtPage' => 'pages', |
99
|
|
|
'project' => $projectData, |
100
|
|
|
'namespaces' => $namespaces, |
101
|
|
|
]); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
/** |
105
|
|
|
* Display the results. |
106
|
|
|
* @Route("/pages/{project}/{username}/{namespace}/{redirects}", name="PagesResult") |
107
|
|
|
* @param string $project The project domain name. |
108
|
|
|
* @param string $username The username. |
109
|
|
|
* @param string $namespace The ID of the namespace. |
110
|
|
|
* @param string $redirects Whether to follow redirects or not. |
111
|
|
|
* @return RedirectResponse|Response |
112
|
|
|
*/ |
113
|
|
|
public function resultAction($project, $username, $namespace = "0", $redirects = "noredirects") |
114
|
|
|
{ |
115
|
|
|
$lh = $this->get("app.labs_helper"); |
116
|
|
|
|
117
|
|
|
$api = $this->get("app.api_helper"); |
118
|
|
|
|
119
|
|
|
$username = ucfirst($username); |
120
|
|
|
|
121
|
|
|
$projectData = ProjectRepository::getProject($project, $this->container); |
122
|
|
|
|
123
|
|
|
// If the project exists, actually populate the values |
124
|
|
View Code Duplication |
if (!$projectData->exists()) { |
|
|
|
|
125
|
|
|
$this->addFlash("notice", ["invalid-project", $project]); |
|
|
|
|
126
|
|
|
return $this->redirectToRoute("pages"); |
127
|
|
|
} |
128
|
|
|
|
129
|
|
|
$dbName = $projectData->getDatabaseName(); |
130
|
|
|
$projectUrl = $projectData->getUrl(); |
131
|
|
|
|
132
|
|
|
$user_id = 0; |
133
|
|
|
|
134
|
|
|
$userTable = $lh->getTable("user", $dbName); |
135
|
|
|
$pageTable = $lh->getTable("page", $dbName); |
136
|
|
|
$pageAssessmentsTable = $lh->getTable("page_assessments", $dbName); |
137
|
|
|
$revisionTable = $lh->getTable("revision", $dbName); |
138
|
|
|
$archiveTable = $lh->getTable("archive", $dbName); |
139
|
|
|
$logTable = $lh->getTable("logging", $dbName, "userindex"); |
140
|
|
|
|
141
|
|
|
// Grab the connection to the replica database (which is separate from the above) |
142
|
|
|
$conn = $this->get('doctrine')->getManager("replicas")->getConnection(); |
143
|
|
|
|
144
|
|
|
// Prepare the query and execute |
145
|
|
|
$resultQuery = $conn->prepare(" |
146
|
|
|
SELECT 'id' AS source, user_id AS value FROM $userTable WHERE user_name = :username |
147
|
|
|
"); |
148
|
|
|
|
149
|
|
|
$resultQuery->bindParam("username", $username); |
150
|
|
|
$resultQuery->execute(); |
151
|
|
|
|
152
|
|
|
$result = $resultQuery->fetchAll(); |
153
|
|
|
|
154
|
|
|
if (isset($result[0]["value"])) { |
155
|
|
|
$user_id = $result[0]["value"]; |
156
|
|
|
} |
157
|
|
|
|
158
|
|
|
$namespaceConditionArc = ""; |
159
|
|
|
$namespaceConditionRev = ""; |
160
|
|
|
|
161
|
|
|
if ($namespace != "all") { |
162
|
|
|
$namespaceConditionRev = " AND page_namespace = '".intval($namespace)."' "; |
163
|
|
|
$namespaceConditionArc = " AND ar_namespace = '".intval($namespace)."' "; |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
$summaryColumns = ['namespace']; // what columns to show in namespace totals table |
167
|
|
|
$redirectCondition = ""; |
168
|
|
|
if ($redirects == "onlyredirects") { |
169
|
|
|
// don't show redundant pages column if only getting data on redirects |
170
|
|
|
$summaryColumns[] = 'redirects'; |
171
|
|
|
|
172
|
|
|
$redirectCondition = " AND page_is_redirect = '1' "; |
173
|
|
|
} elseif ($redirects == "noredirects") { |
174
|
|
|
// don't show redundant redirects column if only getting data on non-redirects |
175
|
|
|
$summaryColumns[] = 'pages'; |
176
|
|
|
|
177
|
|
|
$redirectCondition = " AND page_is_redirect = '0' "; |
178
|
|
|
} else { |
179
|
|
|
// order is important here |
180
|
|
|
$summaryColumns[] = 'pages'; |
181
|
|
|
$summaryColumns[] = 'redirects'; |
182
|
|
|
} |
183
|
|
|
$summaryColumns[] = 'deleted'; // always show deleted column |
184
|
|
|
|
185
|
|
|
if ($user_id == 0) { // IP Editor or undefined username. |
186
|
|
|
$whereRev = " rev_user_text = '$username' AND rev_user = '0' "; |
187
|
|
|
$whereArc = " ar_user_text = '$username' AND ar_user = '0' "; |
188
|
|
|
$having = " rev_user_text = '$username' "; |
189
|
|
|
} else { |
190
|
|
|
$whereRev = " rev_user = '$user_id' AND rev_timestamp > 1 "; |
191
|
|
|
$whereArc = " ar_user = '$user_id' AND ar_timestamp > 1 "; |
192
|
|
|
$having = " rev_user = '$user_id' "; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$hasPageAssessments = $lh->isLabs() && $api->projectHasPageAssessments($project); |
196
|
|
|
$paSelects = $hasPageAssessments ? ', pa_class, pa_importance, pa_page_revision' : ''; |
197
|
|
|
$paSelectsArchive = $hasPageAssessments ? |
198
|
|
|
', NULL AS pa_class, NULL AS pa_page_id, NULL AS pa_page_revision' |
199
|
|
|
: ''; |
200
|
|
|
$paJoin = $hasPageAssessments ? "LEFT JOIN $pageAssessmentsTable ON rev_page = pa_page_id" : ''; |
201
|
|
|
|
202
|
|
|
$stmt = " |
203
|
|
|
(SELECT DISTINCT page_namespace AS namespace, 'rev' AS type, page_title AS page_title, |
204
|
|
|
page_len, page_is_redirect AS page_is_redirect, rev_timestamp AS timestamp, |
205
|
|
|
rev_user, rev_user_text, rev_len, rev_id $paSelects |
206
|
|
|
FROM $pageTable |
207
|
|
|
JOIN $revisionTable ON page_id = rev_page |
208
|
|
|
$paJoin |
209
|
|
|
WHERE $whereRev AND rev_parent_id = '0' $namespaceConditionRev $redirectCondition |
210
|
|
|
" . ($hasPageAssessments ? "GROUP BY rev_page" : "") . " |
211
|
|
|
) |
212
|
|
|
|
213
|
|
|
UNION |
214
|
|
|
|
215
|
|
|
(SELECT a.ar_namespace AS namespace, 'arc' AS type, a.ar_title AS page_title, |
216
|
|
|
0 AS page_len, '0' AS page_is_redirect, min(a.ar_timestamp) AS timestamp, |
217
|
|
|
a.ar_user AS rev_user, a.ar_user_text AS rev_user_text, a.ar_len AS rev_len, |
218
|
|
|
a.ar_rev_id AS rev_id $paSelectsArchive |
219
|
|
|
FROM $archiveTable a |
220
|
|
|
JOIN |
221
|
|
|
( |
222
|
|
|
SELECT b.ar_namespace, b.ar_title |
223
|
|
|
FROM $archiveTable AS b |
224
|
|
|
LEFT JOIN $logTable ON log_namespace = b.ar_namespace AND log_title = b.ar_title |
225
|
|
|
AND log_user = b.ar_user AND (log_action = 'move' or log_action = 'move_redir') |
226
|
|
|
WHERE $whereArc AND b.ar_parent_id = '0' $namespaceConditionArc AND log_action IS NULL |
227
|
|
|
) AS c ON c.ar_namespace= a.ar_namespace AND c.ar_title = a.ar_title |
228
|
|
|
GROUP BY a.ar_namespace, a.ar_title |
229
|
|
|
HAVING $having |
230
|
|
|
) |
231
|
|
|
"; |
232
|
|
|
$resultQuery = $conn->prepare($stmt); |
233
|
|
|
$resultQuery->execute(); |
234
|
|
|
|
235
|
|
|
$result = $resultQuery->fetchAll(); |
236
|
|
|
|
237
|
|
|
$pagesByNamespaceByDate = []; |
238
|
|
|
$pageTitles = []; |
239
|
|
|
$countsByNamespace = []; |
240
|
|
|
$total = 0; |
241
|
|
|
$redirectTotal = 0; |
242
|
|
|
$deletedTotal = 0; |
243
|
|
|
|
244
|
|
|
foreach ($result as $row) { |
245
|
|
|
$datetime = DateTime::createFromFormat('YmdHis', $row["timestamp"]); |
246
|
|
|
$datetimeKey = $datetime->format('Ymdhi'); |
247
|
|
|
$datetimeHuman = $datetime->format('Y-m-d H:i'); |
248
|
|
|
|
249
|
|
|
$pageData = array_merge($row, [ |
250
|
|
|
"human_time" => $datetimeHuman, |
251
|
|
|
"page_title" => str_replace('_', ' ', $row["page_title"]) |
252
|
|
|
]); |
253
|
|
|
|
254
|
|
|
if ($hasPageAssessments) { |
255
|
|
|
$pageData["badge"] = $api->getAssessmentBadgeURL($project, $pageData["pa_class"]); |
256
|
|
|
} |
257
|
|
|
|
258
|
|
|
$pagesByNamespaceByDate[$row["namespace"]][$datetimeKey][] = $pageData; |
259
|
|
|
|
260
|
|
|
$pageTitles[] = $row["page_title"]; |
261
|
|
|
|
262
|
|
|
// Totals |
263
|
|
|
if (isset($countsByNamespace[$row["namespace"]]["total"])) { |
264
|
|
|
$countsByNamespace[$row["namespace"]]["total"]++; |
265
|
|
|
} else { |
266
|
|
|
$countsByNamespace[$row["namespace"]]["total"] = 1; |
267
|
|
|
$countsByNamespace[$row["namespace"]]["redirect"] = 0; |
268
|
|
|
$countsByNamespace[$row["namespace"]]["deleted"] = 0; |
269
|
|
|
} |
270
|
|
|
$total++; |
271
|
|
|
|
272
|
|
View Code Duplication |
if ($row["page_is_redirect"]) { |
|
|
|
|
273
|
|
|
$redirectTotal++; |
274
|
|
|
// Redirects |
275
|
|
|
if (isset($countsByNamespace[$row["namespace"]]["redirect"])) { |
276
|
|
|
$countsByNamespace[$row["namespace"]]["redirect"]++; |
277
|
|
|
} else { |
278
|
|
|
$countsByNamespace[$row["namespace"]]["redirect"] = 1; |
279
|
|
|
} |
280
|
|
|
} |
281
|
|
|
|
282
|
|
View Code Duplication |
if ($row["type"] === "arc") { |
|
|
|
|
283
|
|
|
$deletedTotal++; |
284
|
|
|
// Deleted |
285
|
|
|
if (isset($countsByNamespace[$row["namespace"]]["deleted"])) { |
286
|
|
|
$countsByNamespace[$row["namespace"]]["deleted"]++; |
287
|
|
|
} else { |
288
|
|
|
$countsByNamespace[$row["namespace"]]["deleted"] = 1; |
289
|
|
|
} |
290
|
|
|
} |
291
|
|
|
} |
292
|
|
|
|
293
|
|
View Code Duplication |
if ($total < 1) { |
|
|
|
|
294
|
|
|
$this->addFlash("notice", [ "no-result", $username ]); |
|
|
|
|
295
|
|
|
return $this->redirectToRoute("PagesProject", [ "project"=>$project ]); |
296
|
|
|
} |
297
|
|
|
|
298
|
|
|
ksort($pagesByNamespaceByDate); |
299
|
|
|
ksort($countsByNamespace); |
300
|
|
|
|
301
|
|
|
foreach (array_keys($pagesByNamespaceByDate) as $key) { |
302
|
|
|
krsort($pagesByNamespaceByDate[$key]); |
303
|
|
|
} |
304
|
|
|
|
305
|
|
|
// Retrieve the namespaces |
306
|
|
|
$namespaces = $api->namespaces($project); |
307
|
|
|
|
308
|
|
|
// Assign the values and display the template |
309
|
|
|
return $this->render('pages/result.html.twig', [ |
310
|
|
|
'xtPage' => 'pages', |
311
|
|
|
'xtTitle' => $username, |
312
|
|
|
'project' => $project, |
313
|
|
|
'project_url' => $projectUrl, |
314
|
|
|
|
315
|
|
|
'project' => $project, |
316
|
|
|
'username' => $username, |
317
|
|
|
'namespace' => $namespace, |
318
|
|
|
'redirect' => $redirects, |
319
|
|
|
'summaryColumns' => $summaryColumns, |
320
|
|
|
|
321
|
|
|
'namespaces' => $namespaces, |
322
|
|
|
|
323
|
|
|
'pages' => $pagesByNamespaceByDate, |
324
|
|
|
'count' => $countsByNamespace, |
325
|
|
|
|
326
|
|
|
'total' => $total, |
327
|
|
|
'redirectTotal' => $redirectTotal, |
328
|
|
|
'deletedTotal' => $deletedTotal, |
329
|
|
|
|
330
|
|
|
'hasPageAssessments' => $hasPageAssessments, |
331
|
|
|
]); |
332
|
|
|
} |
333
|
|
|
} |
334
|
|
|
|
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.