Test Failed
Push — master ( 90552d...e0c8a1 )
by MusikAnimal
04:02
created

EditCounterController::generalStatsIndexAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 0
dl 0
loc 4
ccs 0
cts 0
cp 0
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the EditCounterController class.
4
 */
5
6
namespace AppBundle\Controller;
7
8
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
9
use Symfony\Component\DependencyInjection\ContainerInterface;
10
use Symfony\Component\HttpFoundation\JsonResponse;
11
use Symfony\Component\HttpFoundation\RedirectResponse;
12
use Symfony\Component\HttpFoundation\RequestStack;
13
use Symfony\Component\HttpFoundation\Response;
14
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
15
use Xtools\EditCounter;
16
use Xtools\EditCounterRepository;
17
use Xtools\ProjectRepository;
18
19
/**
20
 * Class EditCounterController
21
 */
22
class EditCounterController extends XtoolsController
23
{
24
    /**
25
     * Available statistic sections. These can be hand-picked on the index form so that you only get the data you
26
     * want and hence speed up the tool. Keys are the i18n messages (and DOM IDs), values are the action names.
27
     */
28
    const AVAILABLE_SECTIONS = [
29
        'general-stats' => 'EditCounterGeneralStats',
30
        'namespace-totals' => 'EditCounterNamespaceTotals',
31
        'year-counts' => 'EditCounterYearCounts',
32
        'month-counts' => 'EditCounterMonthCounts',
33
        'timecard' => 'EditCounterRightsChanges',
34
        'top-edited-pages' => 'TopEditsResult',
35
        'rights-changes' => 'EditCounterRightsChanges',
36
        'latest-global-edits' => 'EditCounterLatestGlobalContribs',
37
    ];
38
39
    /** @var EditCounter The edit-counter, that does all the work. */
40
    protected $editCounter;
41
42
    /** @var string[] Which sections to show. */
43
    protected $sections;
44
45
    /**
46
     * Get the name of the tool's index route. This is also the name of the associated model.
47
     * @return string
48
     * @codeCoverageIgnore
49
     */
50
    public function getIndexRoute()
51
    {
52
        return 'EditCounter';
53
    }
54
55
    /**
56
     * EditCounterController constructor.
57
     * @param RequestStack $requestStack
58
     * @param ContainerInterface $container
59
     */
60 1
    public function __construct(RequestStack $requestStack, ContainerInterface $container)
61
    {
62
        // Causes the tool to redirect to the Simple Edit Counter if the user has too high of an edit count.
63 1
        $this->tooHighEditCountAction = 'SimpleEditCounterResult';
64
65
        // The rightsChanges action is exempt from the edit count limitation.
66 1
        $this->tooHighEditCountActionBlacklist = ['rightsChanges'];
67
68 1
        parent::__construct($requestStack, $container);
69
70
        // Now that we have the request object, parse out the requested sections from the URL.
71
        // This could be a pipe-separated string, or an array.
72 1
        $sectionsQuery = $this->request->get('sections', array_keys(self::AVAILABLE_SECTIONS));
73 1
        $this->sections = is_array($sectionsQuery) ? $sectionsQuery : explode('|', $sectionsQuery);
74 1
    }
75
76
    /**
77
     * Every action in this controller (other than 'index') calls this first.
78
     * If a response is returned, the calling action is expected to return it.
79
     * @param string $key API key, as given in the request. Omit this for actions
80
     *   that are public (only /api/ec actions should pass this in).
81
     * @return RedirectResponse|null
82
     * @throws AccessDeniedException If attempting to access internal endpoint.
83
     * @codeCoverageIgnore
84
     */
85
    protected function setUpEditCounter($key = null)
86
    {
87
        // Whether we're making a subrequest (the view makes a request to another action).
88
        // Subrequests to the same controller do not re-instantiate a new controller, and hence
89
        // this flag would not be set in XtoolsController::__construct(), so we must do it here as well.
90
        $this->isSubRequest = $this->request->get('htmlonly')
91
            || $this->get('request_stack')->getParentRequest() !== null;
92
93
        // Return the EditCounter if we already have one.
94
        if ($this->editCounter instanceof EditCounter) {
0 ignored issues
show
introduced by
$this->editCounter is always a sub-type of Xtools\EditCounter. If $this->editCounter can have other possible types, add them to src/AppBundle/Controller/EditCounterController.php:39.
Loading history...
95
            return null;
96
        }
97
98
        // Validate key if attempted to make internal API request.
99
        if ($key && (string)$key !== (string)$this->container->getParameter('secret')) {
100
            throw $this->createAccessDeniedException('This endpoint is for internal use only.');
101
        }
102
103
        // Will redirect to Simple Edit Counter if they have too many edits, as defined self::construct.
104
        $this->validateUser($this->user->getUsername());
105
106
        // Instantiate EditCounter.
107
        $editCounterRepo = new EditCounterRepository();
108
        $editCounterRepo->setContainer($this->container);
109
        $this->editCounter = new EditCounter(
110
            $this->project,
111
            $this->user,
112
            $this->container->get('app.i18n_helper')
113
        );
114
        $this->editCounter->setRepository($editCounterRepo);
115
    }
116
117
    /**
118
     * The initial GET request that displays the search form.
119
     * @Route("/ec", name="EditCounter")
120
     * @Route("/ec/", name="EditCounterSlash")
121
     * @Route("/ec/index.php", name="EditCounterIndexPhp")
122
     * @Route("/ec/{project}", name="EditCounterProject")
123
     * @return RedirectResponse|Response
124
     */
125 1
    public function indexAction()
126
    {
127 1
        if (isset($this->params['project']) && isset($this->params['username'])) {
128
            return $this->redirectFromSections();
129
        }
130
131
        // Otherwise fall through.
132 1
        return $this->render('editCounter/index.html.twig', [
133 1
            'xtPageTitle' => 'tool-editcounter',
134 1
            'xtSubtitle' => 'tool-editcounter-desc',
135 1
            'xtPage' => 'editcounter',
136 1
            'project' => $this->project,
137 1
            'sections' => $this->sections,
138 1
            'availableSections' => array_keys(self::AVAILABLE_SECTIONS),
139
        ]);
140
    }
141
142
    /**
143
     * Redirect to the appropriate action based on what sections are being requested.
144
     * @return RedirectResponse
145
     */
146
    private function redirectFromSections()
147
    {
148
        if (count($this->sections) === 1) {
149
            // Redirect to dedicated route.
150
            return $this->redirectToRoute(self::AVAILABLE_SECTIONS[$this->sections[0]], $this->params);
151
        }
152
153
        // Here we want a pretty URL.
154
        $sectionsParam = implode('|', $this->sections);
155
        return $this->redirect(
156
            $this->generateUrl('EditCounterResult', $this->params).'?sections='.$sectionsParam
157
        );
158
    }
159
160
    /**
161
     * Display all results.
162
     * @Route("/ec/{project}/{username}", name="EditCounterResult")
163
     * @return Response
164
     * @codeCoverageIgnore
165
     */
166
    public function resultAction()
167
    {
168
        $this->setUpEditCounter();
169
170
        $ret = [
171
            'xtTitle' => $this->user->getUsername() . ' - ' . $this->project->getTitle(),
172
            'xtPage' => 'editcounter',
173
            'user' => $this->user,
174
            'project' => $this->project,
175
            'ec' => $this->editCounter,
176
            'sections' => $this->sections,
177
        ];
178
179
        // Used when querying for global rights changes.
180
        if ((bool)$this->container->hasParameter('app.is_labs')) {
181
            $ret['metaProject'] = ProjectRepository::getProject('metawiki', $this->container);
182
        }
183
184
        // Output the relevant format template.
185
        return $this->getFormattedResponse($this->request, 'editCounter/result', $ret);
186
    }
187
188
    /**
189
     * Display the general statistics section.
190
     * @Route("/ec-generalstats/{project}/{username}", name="EditCounterGeneralStats")
191
     * @return Response
192
     * @codeCoverageIgnore
193
     */
194
    public function generalStatsAction()
195
    {
196
        $this->setUpEditCounter();
197
198
        $ret = [
199
            'xtTitle' => $this->user->getUsername(),
200
            'xtPage' => 'editcounter',
201
            'is_sub_request' => $this->isSubRequest,
202
            'user' => $this->user,
203
            'project' => $this->project,
204
            'ec' => $this->editCounter,
205
        ];
206
207
        // Output the relevant format template.
208
        return $this->getFormattedResponse($this->request, 'editCounter/general_stats', $ret);
209
    }
210
211
    /**
212
     * @Route("/ec-generalstats", name="EditCounterGeneralStatsIndex")
213
     * @Route("/ec-generalstats/", name="EditCounterGeneralStatsIndexSlash")
214
     * @return Response
215
     */
216
    public function generalStatsIndexAction()
217
    {
218
        $this->sections = ['general-stats'];
219
        return $this->indexAction();
220
    }
221
222
    /**
223
     * Display the namespace totals section.
224
     * @Route("/ec-namespacetotals/{project}/{username}", name="EditCounterNamespaceTotals")
225
     * @return Response
226
     * @codeCoverageIgnore
227
     */
228
    public function namespaceTotalsAction()
229
    {
230
        $this->setUpEditCounter();
231
232
        $ret = [
233
            'xtTitle' => $this->user->getUsername(),
234
            'xtPage' => 'editcounter',
235
            'is_sub_request' => $this->isSubRequest,
236
            'user' => $this->user,
237
            'project' => $this->project,
238
            'ec' => $this->editCounter,
239
        ];
240
241
        // Output the relevant format template.
242
        return $this->getFormattedResponse($this->request, 'editCounter/namespace_totals', $ret);
243
    }
244
245
    /**
246
     * @Route("/ec-namespacetotals", name="EditCounterNamespaceTotalsIndex")
247
     * @Route("/ec-namespacetotals/", name="EditCounterNamespaceTotalsIndexSlash")
248
     * @return Response
249
     */
250
    public function namespaceTotalsIndexAction()
251
    {
252
        $this->sections = ['namespace-totals'];
253
        return $this->indexAction();
254
    }
255
256
    /**
257
     * Display the timecard section.
258
     * @Route("/ec-timecard/{project}/{username}", name="EditCounterTimecard")
259
     * @return Response
260
     * @codeCoverageIgnore
261
     */
262
    public function timecardAction()
263
    {
264
        $this->setUpEditCounter();
265
266
        $optedInPage = $this->project
267
            ->getRepository()
268
            ->getPage($this->project, $this->project->userOptInPage($this->user));
269
270
        $ret = [
271
            'xtTitle' => $this->user->getUsername(),
272
            'xtPage' => 'editcounter',
273
            'is_sub_request' => $this->isSubRequest,
274
            'user' => $this->user,
275
            'project' => $this->project,
276
            'ec' => $this->editCounter,
277
            'opted_in_page' => $optedInPage,
278
        ];
279
280
        // Output the relevant format template.
281
        return $this->getFormattedResponse($this->request, 'editCounter/timecard', $ret);
282
    }
283
284
    /**
285
     * @Route("/ec-timecard", name="EditCounterTimecardIndex")
286
     * @Route("/ec-timecard/", name="EditCounterTimecardIndexSlash")
287
     * @return Response
288
     */
289
    public function timecardIndexAction()
290
    {
291
        $this->sections = ['timecard'];
292
        return $this->indexAction();
293
    }
294
295
    /**
296
     * Display the year counts section.
297
     * @Route("/ec-yearcounts/{project}/{username}", name="EditCounterYearCounts")
298
     * @return Response
299
     * @codeCoverageIgnore
300
     */
301
    public function yearCountsAction()
302
    {
303
        $this->setUpEditCounter();
304
305
        $ret = [
306
            'xtTitle' => $this->user->getUsername(),
307
            'xtPage' => 'editcounter',
308
            'is_sub_request' => $this->isSubRequest,
309
            'user' => $this->user,
310
            'project' => $this->project,
311
            'ec' => $this->editCounter,
312
        ];
313
314
        // Output the relevant format template.
315
        return $this->getFormattedResponse($this->request, 'editCounter/yearcounts', $ret);
316
    }
317
318
    /**
319
     * @Route("/ec-yearcounts", name="EditCounterYearCountsIndex")
320
     * @Route("/ec-yearcounts/", name="EditCounterYearCountsIndexSlash")
321
     * @return Response
322
     */
323
    public function yearCountsIndexAction()
324
    {
325
        $this->sections = ['year-counts'];
326
        return $this->indexAction();
327
    }
328
329
    /**
330
     * Display the month counts section.
331
     * @Route("/ec-monthcounts/{project}/{username}", name="EditCounterMonthCounts")
332
     * @return Response
333
     * @codeCoverageIgnore
334
     */
335
    public function monthCountsAction()
336
    {
337
        $this->setUpEditCounter();
338
339
        $optedInPage = $this->project
340
            ->getRepository()
341
            ->getPage($this->project, $this->project->userOptInPage($this->user));
342
        $ret = [
343
            'xtTitle' => $this->user->getUsername(),
344
            'xtPage' => 'editcounter',
345
            'is_sub_request' => $this->isSubRequest,
346
            'user' => $this->user,
347
            'project' => $this->project,
348
            'ec' => $this->editCounter,
349
            'opted_in_page' => $optedInPage,
350
        ];
351
352
        // Output the relevant format template.
353
        return $this->getFormattedResponse($this->request, 'editCounter/monthcounts', $ret);
354
    }
355
356
    /**
357
     * @Route("/ec-monthcounts", name="EditCounterMonthCountsIndex")
358
     * @Route("/ec-monthcounts/", name="EditCounterMonthCountsIndexSlash")
359
     * @return Response
360
     */
361
    public function monthCountsIndexAction()
362
    {
363
        $this->sections = ['month-counts'];
364
        return $this->indexAction();
365
    }
366
367
    /**
368
     * Display the user rights changes section.
369
     * @Route("/ec-rightschanges/{project}/{username}", name="EditCounterRightsChanges")
370
     * @return Response
371
     * @codeCoverageIgnore
372
     */
373
    public function rightsChangesAction()
374
    {
375
        $this->setUpEditCounter();
376
377
        $ret = [
378
            'xtTitle' => $this->user->getUsername(),
379
            'xtPage' => 'editcounter',
380
            'is_sub_request' => $this->isSubRequest,
381
            'user' => $this->user,
382
            'project' => $this->project,
383
            'ec' => $this->editCounter,
384
        ];
385
386
        if ((bool)$this->container->hasParameter('app.is_labs')) {
387
            $ret['metaProject'] = ProjectRepository::getProject('metawiki', $this->container);
388
        }
389
390
        // Output the relevant format template.
391
        return $this->getFormattedResponse($this->request, 'editCounter/rights_changes', $ret);
392
    }
393
394
    /**
395
     * @Route("/ec-rightschanges", name="EditCounterRightsChangesIndex")
396
     * @Route("/ec-rightschanges/", name="EditCounterRightsChangesIndexSlash")
397
     * @return Response
398
     */
399
    public function rightsChangesIndexAction()
400
    {
401
        $this->sections = ['rights-changes'];
402
        return $this->indexAction();
403
    }
404
405
    /**
406
     * Display the latest global edits section.
407
     * @Route(
408
     *     "/ec-latestglobal-contributions/{project}/{username}/{offset}",
409
     *     name="EditCounterLatestGlobalContribs",
410
     *     requirements={"offset" = "|\d*"},
411
     *     defaults={"offset" = 0}
412
     * )
413
     * @Route(
414
     *     "/ec-latestglobal/{project}/{username}/{offset}",
415
     *     name="EditCounterLatestGlobal",
416
     *     requirements={"offset" = "|\d*"},
417
     *     defaults={"offset" = 0}
418
     * ),
419
     * @return Response
420
     * @codeCoverageIgnore
421
     */
422
    public function latestGlobalAction()
423
    {
424
        $this->setUpEditCounter();
425
426
        return $this->render('editCounter/latest_global.html.twig', [
427
            'xtTitle' => $this->user->getUsername(),
428
            'xtPage' => 'editcounter',
429
            'is_sub_request' => $this->isSubRequest,
430
            'user' => $this->user,
431
            'project' => $this->project,
432
            'ec' => $this->editCounter,
433
            'offset' => $this->request->get('offset'),
434
            'pageSize' => $this->request->get('pagesize'),
435
        ]);
436
    }
437
438
    /**
439
     * @Route("/ec-latestglobal", name="EditCounterLatestGlobalIndex")
440
     * @Route("/ec-latestglobal/", name="EditCounterLatestGlobalIndexSlash")
441
     * @return Response
442
     */
443
    public function latestGlobalIndexAction()
444
    {
445
        $this->sections = ['latest-global-edits'];
446
        return $this->indexAction();
447
    }
448
449
450
    /**
451
     * Below are internal API endpoints for the Edit Counter.
452
     * All only respond with JSON and only to requests passing in the value
453
     * of the 'secret' parameter. This should not be used in JavaScript or clientside
454
     * applications, rather only used internally.
455
     */
456
457
    /**
458
     * Get (most) of the general statistics as JSON.
459
     * @Route("/api/ec/pairdata/{project}/{username}/{key}", name="EditCounterApiPairData")
460
     * @param string $key API key.
461
     * @return JsonResponse|RedirectResponse
462
     * @codeCoverageIgnore
463
     */
464
    public function pairDataApiAction($key)
465
    {
466
        $this->setUpEditCounter($key);
467
468
        return new JsonResponse(
469
            $this->editCounter->getPairData(),
470
            Response::HTTP_OK
471
        );
472
    }
473
474
    /**
475
     * Get various log counts for the user as JSON.
476
     * @Route("/api/ec/logcounts/{project}/{username}/{key}", name="EditCounterApiLogCounts")
477
     * @param string $key API key.
478
     * @return JsonResponse|RedirectResponse
479
     * @codeCoverageIgnore
480
     */
481
    public function logCountsApiAction($key)
482
    {
483
        $this->setUpEditCounter($key);
484
485
        return new JsonResponse(
486
            $this->editCounter->getLogCounts(),
487
            Response::HTTP_OK
488
        );
489
    }
490
491
    /**
492
     * Get edit sizes for the user as JSON.
493
     * @Route("/api/ec/editsizes/{project}/{username}/{key}", name="EditCounterApiEditSizes")
494
     * @param string $key API key.
495
     * @return JsonResponse|RedirectResponse
496
     * @codeCoverageIgnore
497
     */
498
    public function editSizesApiAction($key)
499
    {
500
        $this->setUpEditCounter($key);
501
502
        return new JsonResponse(
503
            $this->editCounter->getEditSizeData(),
504
            Response::HTTP_OK
505
        );
506
    }
507
508
    /**
509
     * Get the namespace totals for the user as JSON.
510
     * @Route("/api/ec/namespacetotals/{project}/{username}/{key}", name="EditCounterApiNamespaceTotals")
511
     * @param string $key API key.
512
     * @return Response|RedirectResponse
513
     * @codeCoverageIgnore
514
     */
515
    public function namespaceTotalsApiAction($key)
516
    {
517
        $this->setUpEditCounter($key);
518
519
        return new JsonResponse(
520
            $this->editCounter->namespaceTotals(),
521
            Response::HTTP_OK
522
        );
523
    }
524
525
    /**
526
     * Display or fetch the month counts for the user.
527
     * @Route("/api/ec/monthcounts/{project}/{username}/{key}", name="EditCounterApiMonthCounts")
528
     * @param string $key API key.
529
     * @return Response
530
     * @codeCoverageIgnore
531
     */
532
    public function monthCountsApiAction($key)
533
    {
534
        $this->setUpEditCounter($key);
535
536
        return new JsonResponse(
537
            $this->editCounter->monthCounts(),
538
            Response::HTTP_OK
539
        );
540
    }
541
}
542