TopEditsController::tooHighEditCountRoute()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
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\Helper\AutomatedEditsHelper;
8
use App\Model\Edit;
9
use App\Model\TopEdits;
10
use App\Repository\TopEditsRepository;
11
use OpenApi\Annotations as OA;
12
use Symfony\Component\HttpFoundation\JsonResponse;
13
use Symfony\Component\HttpFoundation\Response;
14
use Symfony\Component\Routing\Annotation\Route;
15
16
/**
17
 * The Top Edits tool.
18
 */
19
class TopEditsController extends XtoolsController
20
{
21
    /**
22
     * @inheritDoc
23
     * @codeCoverageIgnore
24
     */
25
    public function getIndexRoute(): string
26
    {
27
        return 'TopEdits';
28
    }
29
30
    /**
31
     * @inheritDoc
32
     * @codeCoverageIgnore
33
     */
34
    public function tooHighEditCountRoute(): string
35
    {
36
        return $this->getIndexRoute();
37
    }
38
39
    /**
40
     * The Top Edits by page action is exempt from the edit count limitation.
41
     * @inheritDoc
42
     * @codeCoverageIgnore
43
     */
44
    public function tooHighEditCountActionAllowlist(): array
45
    {
46
        return ['singlePageTopEdits'];
47
    }
48
49
    /**
50
     * @inheritDoc
51
     * @codeCoverageIgnore
52
     */
53
    public function restrictedApiActions(): array
54
    {
55
        return ['namespaceTopEditsUserApi'];
56
    }
57
58
    /**
59
     * Display the form.
60
     * @Route("/topedits", name="topedits")
61
     * @Route("/topedits", name="TopEdits")
62
     * @Route("/topedits/index.php", name="TopEditsIndex")
63
     * @Route("/topedits/{project}", name="TopEditsProject")
64
     * @return Response
65
     */
66
    public function indexAction(): Response
67
    {
68
        // Redirect if at minimum project and username are provided.
69
        if (isset($this->params['project']) && isset($this->params['username'])) {
70
            if (empty($this->params['page'])) {
71
                return $this->redirectToRoute('TopEditsResultNamespace', $this->params);
72
            }
73
            return $this->redirectToRoute('TopEditsResultPage', $this->params);
74
        }
75
76
        return $this->render('topedits/index.html.twig', array_merge([
77
            'xtPageTitle' => 'tool-topedits',
78
            'xtSubtitle' => 'tool-topedits-desc',
79
            'xtPage' => 'TopEdits',
80
81
            // Defaults that will get overriden if in $params.
82
            'namespace' => 0,
83
            'page' => '',
84
            'username' => '',
85
            'start' => '',
86
            'end' => '',
87
        ], $this->params, ['project' => $this->project]));
88
    }
89
90
    /**
91
     * Every action in this controller (other than 'index') calls this first.
92
     * @param TopEditsRepository $topEditsRepo
93
     * @param AutomatedEditsHelper $autoEditsHelper
94
     * @return TopEdits
95
     * @codeCoverageIgnore
96
     */
97
    public function setUpTopEdits(TopEditsRepository $topEditsRepo, AutomatedEditsHelper $autoEditsHelper): TopEdits
98
    {
99
        return new TopEdits(
100
            $topEditsRepo,
101
            $autoEditsHelper,
102
            $this->project,
103
            $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\TopEdits::__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

103
            /** @scrutinizer ignore-type */ $this->user,
Loading history...
104
            $this->page,
105
            $this->namespace,
106
            $this->start,
107
            $this->end,
108
            $this->limit,
109
            (int)$this->request->query->get('pagination', 0)
110
        );
111
    }
112
113
    /**
114
     * List top edits by this user for all pages in a particular namespace.
115
     * @Route("/topedits/{project}/{username}/{namespace}/{start}/{end}",
116
     *     name="TopEditsResultNamespace",
117
     *     requirements={
118
     *         "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
119
     *         "namespace"="|all|\d+",
120
     *         "start"="|\d{4}-\d{2}-\d{2}",
121
     *         "end"="|\d{4}-\d{2}-\d{2}",
122
     *     },
123
     *     defaults={"namespace" = "all", "start"=false, "end"=false}
124
     * )
125
     * @param TopEditsRepository $topEditsRepo
126
     * @param AutomatedEditsHelper $autoEditsHelper
127
     * @return Response
128
     * @codeCoverageIgnore
129
     */
130
    public function namespaceTopEditsAction(
131
        TopEditsRepository $topEditsRepo,
132
        AutomatedEditsHelper $autoEditsHelper
133
    ): Response {
134
        // Max number of rows per namespace to show. `null` here will use the TopEdits default.
135
        $this->limit = $this->isSubRequest ? 10 : ($this->params['limit'] ?? null);
136
137
        $topEdits = $this->setUpTopEdits($topEditsRepo, $autoEditsHelper);
138
        $topEdits->prepareData();
139
140
        $ret = [
141
            'xtPage' => 'TopEdits',
142
            '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

142
            '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...
143
            'te' => $topEdits,
144
            'is_sub_request' => $this->isSubRequest,
145
        ];
146
147
        // Output the relevant format template.
148
        return $this->getFormattedResponse('topedits/result_namespace', $ret);
149
    }
150
151
    /**
152
     * List top edits by this user for a particular page.
153
     * @Route("/topedits/{project}/{username}/{namespace}/{page}/{start}/{end}",
154
     *     name="TopEditsResultPage",
155
     *     requirements={
156
     *         "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
157
     *         "namespace"="|all|\d+",
158
     *         "page"="(.+?)(?!\/(?:|\d{4}-\d{2}-\d{2})(?:\/(|\d{4}-\d{2}-\d{2}))?)?$",
159
     *         "start"="|\d{4}-\d{2}-\d{2}",
160
     *         "end"="|\d{4}-\d{2}-\d{2}",
161
     *     },
162
     *     defaults={"namespace"="all", "start"=false, "end"=false}
163
     * )
164
     * @param TopEditsRepository $topEditsRepo
165
     * @param AutomatedEditsHelper $autoEditsHelper
166
     * @return Response
167
     * @codeCoverageIgnore
168
     * @todo Add pagination.
169
     */
170
    public function singlePageTopEditsAction(
171
        TopEditsRepository $topEditsRepo,
172
        AutomatedEditsHelper $autoEditsHelper
173
    ): Response {
174
        $topEdits = $this->setUpTopEdits($topEditsRepo, $autoEditsHelper);
175
        $topEdits->prepareData();
176
177
        // Send all to the template.
178
        return $this->getFormattedResponse('topedits/result_page', [
179
            'xtPage' => 'TopEdits',
180
            'xtTitle' => $this->user->getUsername() . ' - ' . $this->page->getTitle(),
0 ignored issues
show
Bug introduced by
The method getTitle() 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

180
            'xtTitle' => $this->user->getUsername() . ' - ' . $this->page->/** @scrutinizer ignore-call */ getTitle(),

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...
181
            'te' => $topEdits,
182
        ]);
183
    }
184
185
    /************************ API endpoints ************************/
186
187
    /**
188
     * Get the most-edited pages by a user.
189
     * @Route("/api/user/top_edits/{project}/{username}/{namespace}/{start}/{end}",
190
     *     name="UserApiTopEditsNamespace",
191
     *     requirements={
192
     *         "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
193
     *         "namespace"="|\d+|all",
194
     *         "start"="|\d{4}-\d{2}-\d{2}",
195
     *         "end"="|\d{4}-\d{2}-\d{2}",
196
     *     },
197
     *     defaults={"namespace"="all", "start"=false, "end"=false},
198
     *     methods={"GET"}
199
     * )
200
     * @OA\Tag(name="User API")
201
     * @OA\Get(description="List the most-edited pages by a user in one or all namespaces.")
202
     * @OA\Parameter(ref="#/components/parameters/Project")
203
     * @OA\Parameter(ref="#/components/parameters/UsernameOrIp")
204
     * @OA\Parameter(ref="#/components/parameters/Namespace")
205
     * @OA\Parameter(ref="#/components/parameters/Start")
206
     * @OA\Parameter(ref="#/components/parameters/End")
207
     * @OA\Parameter(ref="#/components/parameters/Pagination")
208
     * @OA\Response(
209
     *     response=200,
210
     *     description="Most-edited pages, keyed by namespace.",
211
     *     @OA\JsonContent(
212
     *         @OA\Property(property="project", ref="#/components/parameters/Project/schema"),
213
     *         @OA\Property(property="username", ref="#/components/parameters/UsernameOrIp/schema"),
214
     *         @OA\Property(property="namespace", ref="#/components/schemas/Namespace"),
215
     *         @OA\Property(property="start", ref="#/components/parameters/Start/schema"),
216
     *         @OA\Property(property="end", ref="#/components/parameters/End/schema"),
217
     *         @OA\Property(property="top_edits", type="object",
218
     *             @OA\Property(property="namespace ID",
219
     *                 @OA\Property(property="namespace", ref="#/components/schemas/Namespace"),
220
     *                 @OA\Property(property="page_title", ref="#/components/schemas/Page/properties/page_title"),
221
     *                 @OA\Property(property="full_page_title",
222
     *                     ref="#/components/schemas/Page/properties/full_page_title"),
223
     *                 @OA\Property(property="redirect", ref="#/components/schemas/Page/properties/redirect"),
224
     *                 @OA\Property(property="count", type="integer"),
225
     *                 @OA\Property(property="assessment", ref="#/components/schemas/PageAssessment")
226
     *             )
227
     *         )
228
     *     )
229
     * )
230
     * @OA\Response(response=404, ref="#/components/responses/404")
231
     * @OA\Response(response=501, ref="#/components/responses/501")
232
     * @OA\Response(response=503, ref="#/components/responses/503")
233
     * @OA\Response(response=504, ref="#/components/responses/504")
234
     * @param TopEditsRepository $topEditsRepo
235
     * @param AutomatedEditsHelper $autoEditsHelper
236
     * @return JsonResponse
237
     * @codeCoverageIgnore
238
     */
239
    public function namespaceTopEditsUserApiAction(
240
        TopEditsRepository $topEditsRepo,
241
        AutomatedEditsHelper $autoEditsHelper
242
    ): JsonResponse {
243
        $this->recordApiUsage('user/topedits');
244
245
        $topEdits = $this->setUpTopEdits($topEditsRepo, $autoEditsHelper);
246
        $topEdits->prepareData();
247
248
        return $this->getFormattedApiResponse([
249
            'top_edits' => (object)$topEdits->getTopEdits(),
250
        ]);
251
    }
252
253
    /**
254
     * Get the all edits made by a user to a specific page.
255
     * @Route("/api/user/top_edits/{project}/{username}/{namespace}/{page}/{start}/{end}",
256
     *     name="UserApiTopEditsPage",
257
     *     requirements = {
258
     *         "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
259
     *         "namespace"="|\d+|all",
260
     *         "page"="(.+?)(?!\/(?:|\d{4}-\d{2}-\d{2})(?:\/(|\d{4}-\d{2}-\d{2}))?)?$",
261
     *         "start"="|\d{4}-\d{2}-\d{2}",
262
     *         "end"="|\d{4}-\d{2}-\d{2}",
263
     *     },
264
     *     defaults={"namespace"="all", "start"=false, "end"=false},
265
     *     methods={"GET"}
266
     * )
267
     * @OA\Tag(name="User API")
268
     * @OA\Get(description="Get all edits made by a user to a specific page.")
269
     * @OA\Parameter(ref="#/components/parameters/Project")
270
     * @OA\Parameter(ref="#/components/parameters/UsernameOrIp")
271
     * @OA\Parameter(ref="#/components/parameters/Namespace")
272
     * @OA\Parameter(ref="#/components/parameters/PageWithoutNamespace")
273
     * @OA\Parameter(ref="#/components/parameters/Start")
274
     * @OA\Parameter(ref="#/components/parameters/End")
275
     * @OA\Parameter(ref="#/components/parameters/Pagination")
276
     * @OA\Response(
277
     *     response=200,
278
     *     description="Edits to the page",
279
     *     @OA\JsonContent(
280
     *         @OA\Property(property="project", ref="#/components/parameters/Project/schema"),
281
     *         @OA\Property(property="username", ref="#/components/parameters/UsernameOrIp/schema"),
282
     *         @OA\Property(property="namespace", ref="#/components/schemas/Namespace"),
283
     *         @OA\Property(property="start", ref="#/components/parameters/Start/schema"),
284
     *         @OA\Property(property="end", ref="#/components/parameters/End/schema"),
285
     *         @OA\Property(property="top_edits", type="object",
286
     *             @OA\Property(property="namespace ID",
287
     *                 @OA\Property(property="namespace", ref="#/components/schemas/Namespace"),
288
     *                 @OA\Property(property="page_title", ref="#/components/schemas/Page/properties/page_title"),
289
     *                 @OA\Property(property="full_page_title",
290
     *                     ref="#/components/schemas/Page/properties/full_page_title"),
291
     *                 @OA\Property(property="redirect", ref="#/components/schemas/Page/properties/redirect"),
292
     *                 @OA\Property(property="count", type="integer"),
293
     *                 @OA\Property(property="assessment", ref="#/components/schemas/PageAssessment")
294
     *             )
295
     *         )
296
     *     )
297
     * )
298
     * @OA\Response(response=404, ref="#/components/responses/404")
299
     * @OA\Response(response=501, ref="#/components/responses/501")
300
     * @OA\Response(response=503, ref="#/components/responses/503")
301
     * @OA\Response(response=504, ref="#/components/responses/504")
302
     * @param TopEditsRepository $topEditsRepo
303
     * @param AutomatedEditsHelper $autoEditsHelper
304
     * @return JsonResponse
305
     * @codeCoverageIgnore
306
     * @todo Add pagination.
307
     */
308
    public function singlePageTopEditsUserApiAction(
309
        TopEditsRepository $topEditsRepo,
310
        AutomatedEditsHelper $autoEditsHelper
311
    ): JsonResponse {
312
        $this->recordApiUsage('user/topedits');
313
314
        $topEdits = $this->setUpTopEdits($topEditsRepo, $autoEditsHelper);
315
        $topEdits->prepareData();
316
317
        return $this->getFormattedApiResponse([
318
            'top_edits' => array_map(function (Edit $edit) {
319
                return $edit->getForJson();
320
            }, $topEdits->getTopEdits()),
321
        ]);
322
    }
323
}
324