Passed
Pull Request — main (#443)
by MusikAnimal
08:13 queued 04:05
created

nonAutomatedEditsAction()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 2
nc 1
nop 2
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Controller;
6
7
use App\Model\AutoEdits;
8
use App\Repository\AutoEditsRepository;
9
use App\Repository\EditRepository;
10
use Symfony\Component\HttpFoundation\JsonResponse;
11
use Symfony\Component\HttpFoundation\RedirectResponse;
12
use Symfony\Component\HttpFoundation\Response;
13
use Symfony\Component\Routing\Annotation\Route;
14
15
/**
16
 * This controller serves the AutomatedEdits tool.
17
 */
18
class AutomatedEditsController extends XtoolsController
19
{
20
    protected AutoEdits $autoEdits;
21
22
    /** @var array Data that is passed to the view. */
23
    private array $output;
24
25
    /**
26
     * @inheritDoc
27
     * @codeCoverageIgnore
28
     */
29
    public function getIndexRoute(): string
30
    {
31
        return 'AutoEdits';
32
    }
33
34
    /**
35
     * This causes the tool to redirect back to the index page, with an error,
36
     * if the user has too high of an edit count.
37
     * @inheritDoc
38
     * @codeCoverageIgnore
39
     */
40
    public function tooHighEditCountRoute(): string
41
    {
42
        return $this->getIndexRoute();
43
    }
44
45
    /**
46
     * Display the search form.
47
     * @Route("/autoedits", name="AutoEdits")
48
     * @Route("/automatededits", name="AutoEditsLong")
49
     * @Route("/autoedits/index.php", name="AutoEditsIndexPhp")
50
     * @Route("/automatededits/index.php", name="AutoEditsLongIndexPhp")
51
     * @Route("/autoedits/{project}", name="AutoEditsProject")
52
     * @return Response
53
     */
54
    public function indexAction(): Response
55
    {
56
        // Redirect if at minimum project and username are provided.
57
        if (isset($this->params['project']) && isset($this->params['username'])) {
58
            // If 'tool' param is given, redirect to corresponding action.
59
            $tool = $this->request->query->get('tool');
60
61
            if ('all' === $tool) {
62
                unset($this->params['tool']);
63
                return $this->redirectToRoute('AutoEditsContributionsResult', $this->params);
64
            } elseif ('' != $tool && 'none' !== $tool) {
65
                $this->params['tool'] = $tool;
66
                return $this->redirectToRoute('AutoEditsContributionsResult', $this->params);
67
            } elseif ('none' === $tool) {
68
                unset($this->params['tool']);
69
            }
70
71
            // Otherwise redirect to the normal result action.
72
            return $this->redirectToRoute('AutoEditsResult', $this->params);
73
        }
74
75
        return $this->render('autoEdits/index.html.twig', array_merge([
76
            'xtPageTitle' => 'tool-autoedits',
77
            'xtSubtitle' => 'tool-autoedits-desc',
78
            'xtPage' => 'AutoEdits',
79
80
            // Defaults that will get overridden if in $this->params.
81
            'username' => '',
82
            'namespace' => 0,
83
            'start' => '',
84
            'end' => '',
85
        ], $this->params, ['project' => $this->project]));
86
    }
87
88
    /**
89
     * Set defaults, and instantiate the AutoEdits model. This is called at the top of every view action.
90
     * @param AutoEditsRepository $autoEditsRepo
91
     * @param EditRepository $editRepo
92
     * @codeCoverageIgnore
93
     */
94
    private function setupAutoEdits(AutoEditsRepository $autoEditsRepo, EditRepository $editRepo): void
95
    {
96
        $tool = $this->request->query->get('tool', null);
97
        $useSandbox = (bool)$this->request->query->get('usesandbox', false);
98
99
        if ($useSandbox && !$this->request->getSession()->get('logged_in_user')) {
100
            $this->addFlashMessage('danger', 'auto-edits-logged-out');
101
            $useSandbox = false;
102
        }
103
        $autoEditsRepo->setUseSandbox($useSandbox);
104
105
        $misconfigured = $autoEditsRepo->getInvalidTools($this->project);
106
        $helpLink = "https://w.wiki/ppr";
107
        foreach ($misconfigured as $tool) {
108
            $this->addFlashMessage('warning', 'auto-edits-misconfiguration', [$tool, $helpLink]);
109
        }
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
            $autoEditsRepo,
125
            $editRepo,
126
            $this->pageRepo,
127
            $this->userRepo,
128
            $this->project,
129
            $this->user,
0 ignored issues
show
Bug introduced by
It seems like $this->user can also be of type null; however, parameter $user of App\Model\AutoEdits::__construct() does only seem to accept App\Model\User, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

129
            /** @scrutinizer ignore-type */ $this->user,
Loading history...
130
            $this->namespace,
131
            $this->start,
132
            $this->end,
133
            $tool,
134
            $this->offset,
135
            $this->limit
136
        );
137
138
        $this->output = [
139
            'xtPage' => 'AutoEdits',
140
            'xtTitle' => $this->user->getUsername(),
0 ignored issues
show
Bug introduced by
The method getUsername() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

140
            'xtTitle' => $this->user->/** @scrutinizer ignore-call */ getUsername(),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
141
            'ae' => $this->autoEdits,
142
            'is_sub_request' => $this->isSubRequest,
143
        ];
144
    }
145
146
    /**
147
     * Display the results.
148
     * @Route(
149
     *     "/autoedits/{project}/{username}/{namespace}/{start}/{end}/{offset}", name="AutoEditsResult",
150
     *     requirements={
151
     *         "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
152
     *         "namespace"="|all|\d+",
153
     *         "start"="|\d{4}-\d{2}-\d{2}",
154
     *         "end"="|\d{4}-\d{2}-\d{2}",
155
     *         "offset"="|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}",
156
     *     },
157
     *     defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false}
158
     * )
159
     * @param AutoEditsRepository $autoEditsRepo
160
     * @param EditRepository $editRepo
161
     * @return Response
162
     * @codeCoverageIgnore
163
     */
164
    public function resultAction(AutoEditsRepository $autoEditsRepo, EditRepository $editRepo): Response
165
    {
166
        // Will redirect back to index if the user has too high of an edit count.
167
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
168
169
        if (in_array('bot', $this->user->getUserRights($this->project))) {
170
            $this->addFlashMessage('warning', 'auto-edits-bot');
171
        }
172
173
        return $this->getFormattedResponse('autoEdits/result', $this->output);
174
    }
175
176
    /**
177
     * Get non-automated edits for the given user.
178
     * @Route(
179
     *   "/nonautoedits-contributions/{project}/{username}/{namespace}/{start}/{end}/{offset}",
180
     *   name="NonAutoEditsContributionsResult",
181
     *   requirements={
182
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
183
     *       "namespace"="|all|\d+",
184
     *       "start"="|\d{4}-\d{2}-\d{2}",
185
     *       "end"="|\d{4}-\d{2}-\d{2}",
186
     *       "offset"="|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}",
187
     *   },
188
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false}
189
     * )
190
     * @param AutoEditsRepository $autoEditsRepo
191
     * @param EditRepository $editRepo
192
     * @return Response|RedirectResponse
193
     * @codeCoverageIgnore
194
     */
195
    public function nonAutomatedEditsAction(AutoEditsRepository $autoEditsRepo, EditRepository $editRepo): Response
196
    {
197
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
198
        return $this->getFormattedResponse('autoEdits/nonautomated_edits', $this->output);
199
    }
200
201
    /**
202
     * Get automated edits for the given user using the given tool.
203
     * @Route(
204
     *   "/autoedits-contributions/{project}/{username}/{namespace}/{start}/{end}/{offset}",
205
     *   name="AutoEditsContributionsResult",
206
     *   requirements={
207
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
208
     *       "namespace"="|all|\d+",
209
     *       "start"="|\d{4}-\d{2}-\d{2}",
210
     *       "end"="|\d{4}-\d{2}-\d{2}",
211
     *       "offset"="|\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}",
212
     *   },
213
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false}
214
     * )
215
     * @param AutoEditsRepository $autoEditsRepo
216
     * @param EditRepository $editRepo
217
     * @return Response
218
     * @codeCoverageIgnore
219
     */
220
    public function automatedEditsAction(AutoEditsRepository $autoEditsRepo, EditRepository $editRepo): Response
221
    {
222
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
223
224
        return $this->getFormattedResponse('autoEdits/automated_edits', $this->output);
225
    }
226
227
    /************************ API endpoints ************************/
228
229
    /**
230
     * Get a list of the automated tools and their regex/tags/etc.
231
     * @Route("/api/user/automated_tools/{project}", name="UserApiAutoEditsTools")
232
     * @Route("/api/project/automated_tools/{project}", name="ProjectApiAutoEditsTools")
233
     * @param AutoEditsRepository $autoEditsRepo
234
     * @return JsonResponse
235
     * @codeCoverageIgnore
236
     */
237
    public function automatedToolsApiAction(AutoEditsRepository $autoEditsRepo): JsonResponse
238
    {
239
        $this->recordApiUsage('user/automated_tools');
240
        return $this->getFormattedApiResponse($autoEditsRepo->getTools($this->project));
241
    }
242
243
    /**
244
     * Count the number of automated edits the given user has made.
245
     * @Route(
246
     *   "/api/user/automated_editcount/{project}/{username}/{namespace}/{start}/{end}/{tools}",
247
     *   name="UserApiAutoEditsCount",
248
     *   requirements={
249
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
250
     *       "namespace"="|all|\d+",
251
     *       "start"="|\d{4}-\d{2}-\d{2}",
252
     *       "end"="|\d{4}-\d{2}-\d{2}"
253
     *   },
254
     *   defaults={"namespace"="all", "start"=false, "end"=false, "tools"=false}
255
     * )
256
     * @param AutoEditsRepository $autoEditsRepo
257
     * @param EditRepository $editRepo
258
     * @return JsonResponse
259
     * @codeCoverageIgnore
260
     */
261
    public function automatedEditCountApiAction(
262
        AutoEditsRepository $autoEditsRepo,
263
        EditRepository $editRepo
264
    ): JsonResponse {
265
        $this->recordApiUsage('user/automated_editcount');
266
267
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
268
269
        $ret = [
270
            'total_editcount' => $this->autoEdits->getEditCount(),
271
            'automated_editcount' => $this->autoEdits->getAutomatedCount(),
272
        ];
273
        $ret['nonautomated_editcount'] = $ret['total_editcount'] - $ret['automated_editcount'];
274
275
        if (isset($this->params['tools'])) {
276
            $tools = $this->autoEdits->getToolCounts();
277
            $ret['automated_tools'] = $tools;
278
        }
279
280
        return $this->getFormattedApiResponse($ret);
281
    }
282
283
    /**
284
     * Get non-automated edits for the given user.
285
     * @Route(
286
     *   "/api/user/nonautomated_edits/{project}/{username}/{namespace}/{start}/{end}/{offset}",
287
     *   name="UserApiNonAutoEdits",
288
     *   requirements={
289
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
290
     *       "namespace"="|all|\d+",
291
     *       "start"="|\d{4}-\d{2}-\d{2}",
292
     *       "end"="|\d{4}-\d{2}-\d{2}",
293
     *       "offset"="|\d{4}-?\d{2}-?\d{2}T?\d{2}:?\d{2}:?\d{2}"
294
     *   },
295
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false, "limit"=50}
296
     * )
297
     * @param AutoEditsRepository $autoEditsRepo
298
     * @param EditRepository $editRepo
299
     * @return JsonResponse
300
     * @codeCoverageIgnore
301
     */
302
    public function nonAutomatedEditsApiAction(
303
        AutoEditsRepository $autoEditsRepo,
304
        EditRepository $editRepo
305
    ): JsonResponse {
306
        $this->recordApiUsage('user/nonautomated_edits');
307
308
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
309
310
        $out = $this->addFullPageTitlesAndContinue(
311
            'nonautomated_edits',
312
            [],
313
            $this->autoEdits->getNonAutomatedEdits(true)
314
        );
315
316
        return $this->getFormattedApiResponse($out);
317
    }
318
319
    /**
320
     * Get (semi-)automated edits for the given user, optionally using the given tool.
321
     * @Route(
322
     *   "/api/user/automated_edits/{project}/{username}/{namespace}/{start}/{end}/{offset}",
323
     *   name="UserApiAutoEdits",
324
     *   requirements={
325
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
326
     *       "namespace"="|all|\d+",
327
     *       "start"="|\d{4}-\d{2}-\d{2}",
328
     *       "end"="|\d{4}-\d{2}-\d{2}",
329
     *       "offset"="|\d{4}-?\d{2}-?\d{2}T?\d{2}:?\d{2}:?\d{2}",
330
     *   },
331
     *   defaults={"namespace"=0, "start"=false, "end"=false, "offset"=false, "limit"=50}
332
     * )
333
     * @param AutoEditsRepository $autoEditsRepo
334
     * @param EditRepository $editRepo
335
     * @return Response
336
     * @codeCoverageIgnore
337
     */
338
    public function automatedEditsApiAction(AutoEditsRepository $autoEditsRepo, EditRepository $editRepo): Response
339
    {
340
        $this->recordApiUsage('user/automated_edits');
341
342
        $this->setupAutoEdits($autoEditsRepo, $editRepo);
343
344
        $extras = $this->autoEdits->getTool()
345
            ? ['tool' => $this->autoEdits->getTool()]
346
            : [];
347
348
        $out = $this->addFullPageTitlesAndContinue(
349
            'automated_edits',
350
            $extras,
351
            $this->autoEdits->getAutomatedEdits(true)
352
        );
353
354
        return $this->getFormattedApiResponse($out);
355
    }
356
}
357