This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
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
![]() |
|||||
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
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
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. ![]() |
|||||
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
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
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. ![]() |
|||||
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 |