Passed
Push — master ( 06ee4e...702675 )
by MusikAnimal
08:40
created

AutomatedEditsController::indexAction()   B

Complexity

Conditions 7
Paths 5

Size

Total Lines 32
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 21.5206

Importance

Changes 0
Metric Value
cc 7
eloc 20
nc 5
nop 0
dl 0
loc 32
ccs 5
cts 15
cp 0.3333
crap 21.5206
rs 8.6666
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file contains only the AutomatedEditsController class.
4
 */
5
6
declare(strict_types=1);
7
8
namespace AppBundle\Controller;
9
10
use AppBundle\Helper\I18nHelper;
11
use AppBundle\Model\AutoEdits;
12
use AppBundle\Repository\AutoEditsRepository;
13
use Symfony\Component\DependencyInjection\ContainerInterface;
14
use Symfony\Component\HttpFoundation\JsonResponse;
15
use Symfony\Component\HttpFoundation\RedirectResponse;
16
use Symfony\Component\HttpFoundation\RequestStack;
17
use Symfony\Component\HttpFoundation\Response;
18
use Symfony\Component\Routing\Annotation\Route;
19
20
/**
21
 * This controller serves the AutomatedEdits tool.
22
 */
23
class AutomatedEditsController extends XtoolsController
24
{
25
    /** @var AutoEdits The AutoEdits instance. */
26
    protected $autoEdits;
27
28
    /** @var array Data that is passed to the view. */
29
    private $output;
30
31
    /**
32
     * Get the name of the tool's index route.
33
     * This is also the name of the associated model.
34
     * @return string
35
     * @codeCoverageIgnore
36
     */
37
    public function getIndexRoute(): string
38
    {
39
        return 'AutoEdits';
40
    }
41
42
    /**
43
     * AutomatedEditsController constructor.
44
     * @param RequestStack $requestStack
45
     * @param ContainerInterface $container
46
     * @param I18nHelper $i18n
47
     */
48 1
    public function __construct(RequestStack $requestStack, ContainerInterface $container, I18nHelper $i18n)
49
    {
50
        // This will cause the tool to redirect back to the index page, with an error,
51
        // if the user has too high of an edit count.
52 1
        $this->tooHighEditCountAction = $this->getIndexRoute();
53
54 1
        parent::__construct($requestStack, $container, $i18n);
55 1
    }
56
57
    /**
58
     * Display the search form.
59
     * @Route("/autoedits", name="AutoEdits")
60
     * @Route("/automatededits", name="AutoEditsLong")
61
     * @Route("/autoedits/index.php", name="AutoEditsIndexPhp")
62
     * @Route("/automatededits/index.php", name="AutoEditsLongIndexPhp")
63
     * @Route("/autoedits/{project}", name="AutoEditsProject")
64
     * @return Response
65
     */
66 1
    public function indexAction(): Response
67
    {
68
        // Redirect if at minimum project and username are provided.
69 1
        if (isset($this->params['project']) && isset($this->params['username'])) {
70
            // If 'tool' param is given, redirect to corresponding action.
71
            $tool = $this->request->query->get('tool');
72
73
            if ('all' === $tool) {
74
                unset($this->params['tool']);
75
                return $this->redirectToRoute('AutoEditsContributionsResult', $this->params);
76
            } elseif ('' != $tool && 'none' !== $tool) {
77
                $this->params['tool'] = $tool;
78
                return $this->redirectToRoute('AutoEditsContributionsResult', $this->params);
79
            } elseif ('none' === $tool) {
80
                unset($this->params['tool']);
81
            }
82
83
            // Otherwise redirect to the normal result action.
84
            return $this->redirectToRoute('AutoEditsResult', $this->params);
85
        }
86
87 1
        return $this->render('autoEdits/index.html.twig', array_merge([
88 1
            'xtPageTitle' => 'tool-autoedits',
89
            'xtSubtitle' => 'tool-autoedits-desc',
90
            'xtPage' => 'AutoEdits',
91
92
            // Defaults that will get overridden if in $this->params.
93
            'username' => '',
94
            'namespace' => 0,
95
            'start' => '',
96
            'end' => '',
97 1
        ], $this->params, ['project' => $this->project]));
98
    }
99
100
    /**
101
     * Set defaults, and instantiate the AutoEdits model. This is called at the top of every view action.
102
     * @codeCoverageIgnore
103
     */
104
    private function setupAutoEdits(): void
105
    {
106
        $tool = $this->request->query->get('tool', null);
107
108
        $autoEditsRepo = new AutoEditsRepository();
109
        $autoEditsRepo->setContainer($this->container);
110
111
        // Validate tool.
112
        // FIXME: instead of redirecting to index page, show result page listing all tools for that project,
113
        //  clickable to show edits by the user, etc.
114
        if ($tool && !isset($autoEditsRepo->getTools($this->project)[$tool])) {
115
            $this->throwXtoolsException(
116
                $this->getIndexRoute(),
117
                'auto-edits-unknown-tool',
118
                [$tool],
119
                'tool'
120
            );
121
        }
122
123
        $this->autoEdits = new AutoEdits(
124
            $this->project,
125
            $this->user,
126
            $this->namespace,
127
            $this->start,
128
            $this->end,
129
            $tool,
130
            $this->offset
131
        );
132
        $this->autoEdits->setRepository($autoEditsRepo);
133
134
        $this->output = [
135
            'xtPage' => 'AutoEdits',
136
            'xtTitle' => $this->user->getUsername(),
137
            'ae' => $this->autoEdits,
138
            'is_sub_request' => $this->isSubRequest,
139
        ];
140
    }
141
142
    /**
143
     * Display the results.
144
     * @Route(
145
     *     "/autoedits/{project}/{username}/{namespace}/{start}/{end}", name="AutoEditsResult",
146
     *     requirements={
147
     *         "namespace"="|all|\d+",
148
     *         "start"="|\d{4}-\d{2}-\d{2}",
149
     *         "end"="|\d{4}-\d{2}-\d{2}",
150
     *     },
151
     *     defaults={"namespace"=0, "start"=false, "end"=false}
152
     * )
153
     * @return Response
154
     * @codeCoverageIgnore
155
     */
156
    public function resultAction(): Response
157
    {
158
        // Will redirect back to index if the user has too high of an edit count.
159
        $this->setupAutoEdits();
160
161
        if (in_array('bot', $this->user->getUserRights($this->project))) {
162
            $this->addFlashMessage('warning', 'auto-edits-bot');
163
        }
164
165
        return $this->getFormattedResponse('autoEdits/result', $this->output);
166
    }
167
168
    /**
169
     * Get non-automated edits for the given user.
170
     * @Route(
171
     *   "/nonautoedits-contributions/{project}/{username}/{namespace}/{start}/{end}/{offset}",
172
     *   name="NonAutoEditsContributionsResult",
173
     *   requirements={
174
     *       "namespace"="|all|\d+",
175
     *       "start"="|\d{4}-\d{2}-\d{2}",
176
     *       "end"="|\d{4}-\d{2}-\d{2}",
177
     *       "offset"="\d*"
178
     *   },
179
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=0}
180
     * )
181
     * @return Response|RedirectResponse
182
     * @codeCoverageIgnore
183
     */
184
    public function nonAutomatedEditsAction(): Response
185
    {
186
        $this->setupAutoEdits();
187
188
        return $this->getFormattedResponse('autoEdits/nonautomated_edits', $this->output);
189
    }
190
191
    /**
192
     * Get automated edits for the given user using the given tool.
193
     * @Route(
194
     *   "/autoedits-contributions/{project}/{username}/{namespace}/{start}/{end}/{offset}",
195
     *   name="AutoEditsContributionsResult",
196
     *   requirements={
197
     *       "namespace"="|all|\d+",
198
     *       "start"="|\d{4}-\d{2}-\d{2}",
199
     *       "end"="|\d{4}-\d{2}-\d{2}",
200
     *       "offset"="\d*"
201
     *   },
202
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=0}
203
     * )
204
     * @return Response|RedirectResponse
205
     * @codeCoverageIgnore
206
     */
207
    public function automatedEditsAction(): Response
208
    {
209
        $this->setupAutoEdits();
210
211
        return $this->getFormattedResponse('autoEdits/automated_edits', $this->output);
212
    }
213
214
    /************************ API endpoints ************************/
215
216
    /**
217
     * Get a list of the automated tools and their regex/tags/etc.
218
     * @Route("/api/user/automated_tools/{project}", name="UserApiAutoEditsTools")
219
     * @Route("/api/project/automated_tools/{project}", name="ProjectApiAutoEditsTools")
220
     * @return JsonResponse
221
     * @codeCoverageIgnore
222
     */
223
    public function automatedToolsApiAction(): JsonResponse
224
    {
225
        $this->recordApiUsage('user/automated_tools');
226
227
        $aeh = $this->container->get('app.automated_edits_helper');
228
        return $this->getFormattedApiResponse($aeh->getTools($this->project));
229
    }
230
231
    /**
232
     * Count the number of automated edits the given user has made.
233
     * @Route(
234
     *   "/api/user/automated_editcount/{project}/{username}/{namespace}/{start}/{end}/{tools}",
235
     *   name="UserApiAutoEditsCount",
236
     *   requirements={
237
     *       "namespace"="|all|\d+",
238
     *       "start"="|\d{4}-\d{2}-\d{2}",
239
     *       "end"="|\d{4}-\d{2}-\d{2}"
240
     *   },
241
     *   defaults={"namespace"="all", "start"=false, "end"=false}
242
     * )
243
     * @param string $tools Non-blank to show which tools were used and how many times.
244
     * @return JsonResponse
245
     * @codeCoverageIgnore
246
     */
247
    public function automatedEditCountApiAction(string $tools = ''): JsonResponse
248
    {
249
        $this->recordApiUsage('user/automated_editcount');
250
251
        $this->setupAutoEdits();
252
253
        $ret = [
254
            'total_editcount' => $this->autoEdits->getEditCount(),
255
            'automated_editcount' => $this->autoEdits->getAutomatedCount(),
256
        ];
257
        $ret['nonautomated_editcount'] = $ret['total_editcount'] - $ret['automated_editcount'];
258
259
        if ('' != $tools) {
260
            $tools = $this->autoEdits->getToolCounts();
261
            $ret['automated_tools'] = $tools;
262
        }
263
264
        return $this->getFormattedApiResponse($ret);
265
    }
266
267
    /**
268
     * Get non-automated edits for the given user.
269
     * @Route(
270
     *   "/api/user/nonautomated_edits/{project}/{username}/{namespace}/{start}/{end}/{offset}",
271
     *   name="UserApiNonAutoEdits",
272
     *   requirements={
273
     *       "namespace"="|all|\d+",
274
     *       "start"="|\d{4}-\d{2}-\d{2}",
275
     *       "end"="|\d{4}-\d{2}-\d{2}",
276
     *       "offset"="\d*"
277
     *   },
278
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=0}
279
     * )
280
     * @return JsonResponse
281
     * @codeCoverageIgnore
282
     */
283
    public function nonAutomatedEditsApiAction(): JsonResponse
284
    {
285
        $this->recordApiUsage('user/nonautomated_edits');
286
287
        $this->setupAutoEdits();
288
289
        $ret = array_map(function ($rev) {
290
            return array_merge([
291
                'full_page_title' => $this->getPageFromNsAndTitle(
292
                    (int)$rev['page_namespace'],
293
                    $rev['page_title'],
294
                    true
295
                ),
296
            ], $rev);
297
        }, $this->autoEdits->getNonAutomatedEdits(true));
298
299
        return $this->getFormattedApiResponse(['nonautomated_edits' => $ret]);
300
    }
301
302
    /**
303
     * Get (semi-)automated edits for the given user, optionally using the given tool.
304
     * @Route(
305
     *   "/api/user/automated_edits/{project}/{username}/{namespace}/{start}/{end}/{offset}",
306
     *   name="UserApiAutoEdits",
307
     *   requirements={
308
     *       "namespace"="|all|\d+",
309
     *       "start"="|\d{4}-\d{2}-\d{2}",
310
     *       "end"="|\d{4}-\d{2}-\d{2}",
311
     *       "offset"="\d*"
312
     *   },
313
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=0}
314
     * )
315
     * @return Response
316
     * @codeCoverageIgnore
317
     */
318
    public function automatedEditsApiAction(): Response
319
    {
320
        $this->recordApiUsage('user/automated_edits');
321
322
        $this->setupAutoEdits();
323
324
        $ret = [];
325
326
        if ($this->autoEdits->getTool()) {
327
            $ret['tool'] = $this->autoEdits->getTool();
328
        }
329
330
        $ret['automated_edits'] = array_map(function ($rev) {
331
            return array_merge([
332
                'full_page_title' => $this->getPageFromNsAndTitle(
333
                    (int)$rev['page_namespace'],
334
                    $rev['page_title'],
335
                    true
336
                ),
337
            ], $rev);
338
        }, $this->autoEdits->getAutomatedEdits(true));
339
340
        return $this->getFormattedApiResponse($ret);
341
    }
342
}
343