Passed
Pull Request — main (#425)
by MusikAnimal
05:22 queued 01:08
created

AutomatedEditsController::addFullPageTitlesAndContinue()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 22
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
cc 3
eloc 12
c 0
b 0
f 0
nc 2
nop 3
dl 0
loc 22
ccs 0
cts 8
cp 0
crap 12
rs 9.8666
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
    public function __construct(RequestStack $requestStack, ContainerInterface $container, I18nHelper $i18n)
49 1
    {
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
        $this->tooHighEditCountAction = $this->getIndexRoute();
53 1
54
        parent::__construct($requestStack, $container, $i18n);
55 1
    }
56 1
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
    public function indexAction(): Response
67 1
    {
68
        // Redirect if at minimum project and username are provided.
69
        if (isset($this->params['project']) && isset($this->params['username'])) {
70 1
            // 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
        return $this->render('autoEdits/index.html.twig', array_merge([
88 1
            'xtPageTitle' => 'tool-autoedits',
89 1
            '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
        ], $this->params, ['project' => $this->project]));
98 1
    }
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
        $useSandbox = (bool)$this->request->query->get('usesandbox', false);
108
109
        if ($useSandbox && !$this->request->getSession()->get('logged_in_user')) {
110
            $this->addFlashMessage('danger', 'auto-edits-logged-out');
111
            $useSandbox = false;
112
        }
113
114
        $autoEditsRepo = new AutoEditsRepository($useSandbox);
115
        $autoEditsRepo->setContainer($this->container);
116
117
        $misconfigured = $autoEditsRepo->getInvalidTools($this->project);
118
        $helpLink = "https://w.wiki/ppr";
119
        foreach ($misconfigured as $tool) {
120
            $this->addFlashMessage('warning', 'auto-edits-misconfiguration', [$tool, $helpLink]);
121
        }
122
123
        // Validate tool.
124
        // FIXME: instead of redirecting to index page, show result page listing all tools for that project,
125
        //  clickable to show edits by the user, etc.
126
        if ($tool && !isset($autoEditsRepo->getTools($this->project)[$tool])) {
127
            $this->throwXtoolsException(
128
                $this->getIndexRoute(),
129
                'auto-edits-unknown-tool',
130
                [$tool],
131
                'tool'
132
            );
133
        }
134
135
        $this->autoEdits = new AutoEdits(
136
            $this->project,
137
            $this->user,
138
            $this->namespace,
139
            $this->start,
140
            $this->end,
141
            $tool,
142
            $this->offset,
143
            $this->limit
144
        );
145
        $this->autoEdits->setRepository($autoEditsRepo);
146
147
        $this->output = [
148
            'xtPage' => 'AutoEdits',
149
            'xtTitle' => $this->user->getUsername(),
150
            'ae' => $this->autoEdits,
151
            'is_sub_request' => $this->isSubRequest,
152
        ];
153
    }
154
155
    /**
156
     * Display the results.
157
     * @Route(
158
     *     "/autoedits/{project}/{username}/{namespace}/{start}/{end}/{offset}", name="AutoEditsResult",
159
     *     requirements={
160
     *         "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
161
     *         "namespace"="|all|\d+",
162
     *         "start"="|\d{4}-\d{2}-\d{2}",
163
     *         "end"="|\d{4}-\d{2}-\d{2}",
164
     *         "offset"="|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}",
165
     *     },
166
     *     defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false}
167
     * )
168
     * @return Response
169
     * @codeCoverageIgnore
170
     */
171
    public function resultAction(): Response
172
    {
173
        // Will redirect back to index if the user has too high of an edit count.
174
        $this->setupAutoEdits();
175
176
        if (in_array('bot', $this->user->getUserRights($this->project))) {
177
            $this->addFlashMessage('warning', 'auto-edits-bot');
178
        }
179
180
        return $this->getFormattedResponse('autoEdits/result', $this->output);
181
    }
182
183
    /**
184
     * Get non-automated edits for the given user.
185
     * @Route(
186
     *   "/nonautoedits-contributions/{project}/{username}/{namespace}/{start}/{end}/{offset}",
187
     *   name="NonAutoEditsContributionsResult",
188
     *   requirements={
189
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
190
     *       "namespace"="|all|\d+",
191
     *       "start"="|\d{4}-\d{2}-\d{2}",
192
     *       "end"="|\d{4}-\d{2}-\d{2}",
193
     *       "offset"="|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}",
194
     *   },
195
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false}
196
     * )
197
     * @return Response|RedirectResponse
198
     * @codeCoverageIgnore
199
     */
200
    public function nonAutomatedEditsAction(): Response
201
    {
202
        $this->setupAutoEdits();
203
204
        return $this->getFormattedResponse('autoEdits/nonautomated_edits', $this->output);
205
    }
206
207
    /**
208
     * Get automated edits for the given user using the given tool.
209
     * @Route(
210
     *   "/autoedits-contributions/{project}/{username}/{namespace}/{start}/{end}/{offset}",
211
     *   name="AutoEditsContributionsResult",
212
     *   requirements={
213
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
214
     *       "namespace"="|all|\d+",
215
     *       "start"="|\d{4}-\d{2}-\d{2}",
216
     *       "end"="|\d{4}-\d{2}-\d{2}",
217
     *       "offset"="|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}",
218
     *   },
219
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false}
220
     * )
221
     * @return Response
222
     * @codeCoverageIgnore
223
     */
224
    public function automatedEditsAction(): Response
225
    {
226
        $this->setupAutoEdits();
227
228
        return $this->getFormattedResponse('autoEdits/automated_edits', $this->output);
229
    }
230
231
    /************************ API endpoints ************************/
232
233
    /**
234
     * Get a list of the automated tools and their regex/tags/etc.
235
     * @Route("/api/user/automated_tools/{project}", name="UserApiAutoEditsTools")
236
     * @Route("/api/project/automated_tools/{project}", name="ProjectApiAutoEditsTools")
237
     * @return JsonResponse
238
     * @codeCoverageIgnore
239
     */
240
    public function automatedToolsApiAction(): JsonResponse
241
    {
242
        $this->recordApiUsage('user/automated_tools');
243
244
        $aeh = $this->container->get('app.automated_edits_helper');
245
        return $this->getFormattedApiResponse($aeh->getTools($this->project));
246
    }
247
248
    /**
249
     * Count the number of automated edits the given user has made.
250
     * @Route(
251
     *   "/api/user/automated_editcount/{project}/{username}/{namespace}/{start}/{end}/{tools}",
252
     *   name="UserApiAutoEditsCount",
253
     *   requirements={
254
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
255
     *       "namespace"="|all|\d+",
256
     *       "start"="|\d{4}-\d{2}-\d{2}",
257
     *       "end"="|\d{4}-\d{2}-\d{2}"
258
     *   },
259
     *   defaults={"namespace"="all", "start"=false, "end"=false, "tools"=false}
260
     * )
261
     * @return JsonResponse
262
     * @codeCoverageIgnore
263
     */
264
    public function automatedEditCountApiAction(): JsonResponse
265
    {
266
        $this->recordApiUsage('user/automated_editcount');
267
268
        $this->setupAutoEdits();
269
270
        $ret = [
271
            'total_editcount' => $this->autoEdits->getEditCount(),
272
            'automated_editcount' => $this->autoEdits->getAutomatedCount(),
273
        ];
274
        $ret['nonautomated_editcount'] = $ret['total_editcount'] - $ret['automated_editcount'];
275
276
        if (isset($this->params['tools'])) {
277
            $tools = $this->autoEdits->getToolCounts();
278
            $ret['automated_tools'] = $tools;
279
        }
280
281
        return $this->getFormattedApiResponse($ret);
282
    }
283
284
    /**
285
     * Get non-automated edits for the given user.
286
     * @Route(
287
     *   "/api/user/nonautomated_edits/{project}/{username}/{namespace}/{start}/{end}/{offset}",
288
     *   name="UserApiNonAutoEdits",
289
     *   requirements={
290
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
291
     *       "namespace"="|all|\d+",
292
     *       "start"="|\d{4}-\d{2}-\d{2}",
293
     *       "end"="|\d{4}-\d{2}-\d{2}",
294
     *       "offset"="|\d{4}-?\d{2}-?\d{2}T?\d{2}:?\d{2}:?\d{2}"
295
     *   },
296
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false, "limit"=50}
297
     * )
298
     * @return JsonResponse
299
     * @codeCoverageIgnore
300
     */
301
    public function nonAutomatedEditsApiAction(): JsonResponse
302
    {
303
        $this->recordApiUsage('user/nonautomated_edits');
304
305
        $this->setupAutoEdits();
306
307
        $out = $this->addFullPageTitlesAndContinue(
308
            'nonautomated_edits',
309
            [],
310
            $this->autoEdits->getNonAutomatedEdits(true)
311
        );
312
313
        return $this->getFormattedApiResponse($out);
314
    }
315
316
    /**
317
     * Get (semi-)automated edits for the given user, optionally using the given tool.
318
     * @Route(
319
     *   "/api/user/automated_edits/{project}/{username}/{namespace}/{start}/{end}/{offset}",
320
     *   name="UserApiAutoEdits",
321
     *   requirements={
322
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
323
     *       "namespace"="|all|\d+",
324
     *       "start"="|\d{4}-\d{2}-\d{2}",
325
     *       "end"="|\d{4}-\d{2}-\d{2}",
326
     *       "offset"="|\d{4}-?\d{2}-?\d{2}T?\d{2}:?\d{2}:?\d{2}",
327
     *   },
328
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false, "limit"=50}
329
     * )
330
     * @return Response
331
     * @codeCoverageIgnore
332
     */
333
    public function automatedEditsApiAction(): Response
334
    {
335
        $this->recordApiUsage('user/automated_edits');
336
337
        $this->setupAutoEdits();
338
339
        $extras = $this->autoEdits->getTool()
340
            ? ['tool' => $this->autoEdits->getTool()]
341
            : [];
342
343
        $out = $this->addFullPageTitlesAndContinue(
344
            'automated_edits',
345
            $extras,
346
            $this->autoEdits->getAutomatedEdits(true)
347
        );
348
349
        return $this->getFormattedApiResponse($out);
350
    }
351
}
352