Passed
Pull Request — main (#442)
by MusikAnimal
08:15 queued 04:14
created

CategoryEditsController::tooHighEditCountRoute()   A

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\Exception\XtoolsHttpException;
8
use App\Model\CategoryEdits;
9
use App\Repository\CategoryEditsRepository;
10
use Symfony\Component\HttpFoundation\JsonResponse;
11
use Symfony\Component\HttpFoundation\Response;
12
use Symfony\Component\Routing\Annotation\Route;
13
14
/**
15
 * This controller serves the Category Edits tool.
16
 */
17
class CategoryEditsController extends XtoolsController
18
{
19
    protected CategoryEdits $categoryEdits;
20
21
    /** @var string[] The categories, with or without namespace. */
22
    protected array $categories;
23
24
    /** @var array Data that is passed to the view. */
25
    private array $output;
26
27
    /**
28
     * @inheritDoc
29
     * @codeCoverageIgnore
30
     */
31
    public function getIndexRoute(): string
32
    {
33
        return 'CategoryEdits';
34
    }
35
36
    /**
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("/categoryedits", name="CategoryEdits")
48
     * @Route("/categoryedits/{project}", name="CategoryEditsProject")
49
     * @return Response
50
     * @codeCoverageIgnore
51
     */
52
    public function indexAction(): Response
53
    {
54
        // Redirect if at minimum project, username and categories are provided.
55
        if (isset($this->params['project']) && isset($this->params['username']) && isset($this->params['categories'])) {
56
            return $this->redirectToRoute('CategoryEditsResult', $this->params);
57
        }
58
59
        return $this->render('categoryEdits/index.html.twig', array_merge([
60
            'xtPageTitle' => 'tool-categoryedits',
61
            'xtSubtitle' => 'tool-categoryedits-desc',
62
            'xtPage' => 'CategoryEdits',
63
64
            // Defaults that will get overridden if in $params.
65
            'namespace' => 0,
66
            'start' => '',
67
            'end' => '',
68
            'username' => '',
69
            'categories' => '',
70
        ], $this->params, ['project' => $this->project]));
71
    }
72
73
    /**
74
     * Set defaults, and instantiate the CategoryEdits model. This is called at the top of every view action.
75
     * @codeCoverageIgnore
76
     */
77
    private function setupCategoryEdits(CategoryEditsRepository $categoryEditsRepo): void
78
    {
79
        $this->extractCategories();
80
81
        $this->categoryEdits = new CategoryEdits(
82
            $categoryEditsRepo,
83
            $this->project,
84
            $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\CategoryEdits::__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

84
            /** @scrutinizer ignore-type */ $this->user,
Loading history...
85
            $this->categories,
86
            $this->start,
87
            $this->end,
88
            $this->offset
89
        );
90
91
        $this->output = [
92
            'xtPage' => 'CategoryEdits',
93
            '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

93
            '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...
94
            'project' => $this->project,
95
            'user' => $this->user,
96
            'ce' => $this->categoryEdits,
97
            'is_sub_request' => $this->isSubRequest,
98
        ];
99
    }
100
101
    /**
102
     * Go through the categories and normalize values, and set them on class properties.
103
     * @codeCoverageIgnore
104
     */
105
    private function extractCategories(): void
106
    {
107
        // Split categories by pipe.
108
        $categories = explode('|', $this->request->get('categories'));
0 ignored issues
show
Bug introduced by
It seems like $this->request->get('categories') can also be of type null; however, parameter $string of explode() does only seem to accept string, 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

108
        $categories = explode('|', /** @scrutinizer ignore-type */ $this->request->get('categories'));
Loading history...
109
110
        // Loop through the given categories, stripping out the namespace.
111
        // If a namespace was removed, it is flagged it as normalize
112
        // We look for the wiki's category namespace name, and the MediaWiki default
113
        // 'Category:', which sometimes is used cross-wiki (because it still works).
114
        $normalized = false;
115
        $nsName = $this->project->getNamespaces()[14].':';
116
        $this->categories = array_map(function ($category) use ($nsName, &$normalized) {
117
            if (0 === strpos($category, $nsName) || 0 === strpos($category, 'Category:')) {
118
                $normalized = true;
119
            }
120
            return preg_replace('/^'.$nsName.'/', '', $category);
121
        }, $categories);
122
123
        // Redirect if normalized, since we don't want the Category: prefix in the URL.
124
        if ($normalized) {
125
            throw new XtoolsHttpException(
126
                '',
127
                $this->generateUrl($this->request->get('_route'), array_merge(
0 ignored issues
show
Bug introduced by
It seems like $this->request->get('_route') can also be of type null; however, parameter $route of Symfony\Bundle\Framework...ntroller::generateUrl() does only seem to accept string, 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

127
                $this->generateUrl(/** @scrutinizer ignore-type */ $this->request->get('_route'), array_merge(
Loading history...
128
                    $this->request->attributes->get('_route_params'),
129
                    ['categories' => implode('|', $this->categories)]
130
                ))
131
            );
132
        }
133
    }
134
135
    /**
136
     * Display the results.
137
     * @Route(
138
     *     "/categoryedits/{project}/{username}/{categories}/{start}/{end}/{offset}",
139
     *     name="CategoryEditsResult",
140
     *     requirements={
141
     *         "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
142
     *         "categories"="(.+?)(?!\/(?:|\d{4}-\d{2}-\d{2})(?:\/(|\d{4}-\d{2}-\d{2}))?)?$",
143
     *         "start"="|\d{4}-\d{2}-\d{2}",
144
     *         "end"="|\d{4}-\d{2}-\d{2}",
145
     *         "offset"="|\d{4}-?\d{2}-?\d{2}T?\d{2}:?\d{2}:?\d{2}",
146
     *     },
147
     *     defaults={"start"=false, "end"=false, "offset"=false}
148
     * )
149
     * @param CategoryEditsRepository $categoryEditsRepo
150
     * @return Response
151
     * @codeCoverageIgnore
152
     */
153
    public function resultAction(CategoryEditsRepository $categoryEditsRepo): Response
154
    {
155
        $this->setupCategoryEdits($categoryEditsRepo);
156
157
        return $this->getFormattedResponse('categoryEdits/result', $this->output);
158
    }
159
160
    /**
161
     * Get edits by a user to pages in given categories.
162
     * @Route(
163
     *   "/categoryedits-contributions/{project}/{username}/{categories}/{start}/{end}/{offset}",
164
     *   name="CategoryContributionsResult",
165
     *   requirements={
166
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
167
     *       "categories"="(.+?)(?!\/(?:|\d{4}-\d{2}-\d{2}))?",
168
     *       "start"="|\d{4}-\d{2}-\d{2}",
169
     *       "end"="|\d{4}-\d{2}-\d{2}",
170
     *       "offset"="|\d{4}-?\d{2}-?\d{2}T?\d{2}:?\d{2}:?\d{2}",
171
     *   },
172
     *   defaults={"start"=false, "end"=false, "offset"=false}
173
     * )
174
     * @param CategoryEditsRepository $categoryEditsRepo
175
     * @return Response
176
     * @codeCoverageIgnore
177
     */
178
    public function categoryContributionsAction(CategoryEditsRepository $categoryEditsRepo): Response
179
    {
180
        $this->setupCategoryEdits($categoryEditsRepo);
181
182
        return $this->render('categoryEdits/contributions.html.twig', $this->output);
183
    }
184
185
    /************************ API endpoints ************************/
186
187
    /**
188
     * Count the number of category edits the given user has made.
189
     * @Route(
190
     *   "/api/user/category_editcount/{project}/{username}/{categories}/{start}/{end}",
191
     *   name="UserApiCategoryEditCount",
192
     *   requirements={
193
     *       "username" = "(ipr-.+\/\d+[^\/])|([^\/]+)",
194
     *       "categories" = "(.+?)(?!\/(?:|\d{4}-\d{2}-\d{2})(?:\/(|\d{4}-\d{2}-\d{2}))?)?$",
195
     *       "start" = "|\d{4}-\d{2}-\d{2}",
196
     *       "end" = "|\d{4}-\d{2}-\d{2}"
197
     *   },
198
     *   defaults={"start" = false, "end" = false}
199
     * )
200
     * @param CategoryEditsRepository $categoryEditsRepo
201
     * @return JsonResponse
202
     * @codeCoverageIgnore
203
     */
204
    public function categoryEditCountApiAction(CategoryEditsRepository $categoryEditsRepo): JsonResponse
205
    {
206
        $this->recordApiUsage('user/category_editcount');
207
208
        $this->setupCategoryEdits($categoryEditsRepo);
209
210
        $ret = [
211
            'total_editcount' => $this->categoryEdits->getEditCount(),
212
            'category_editcount' => $this->categoryEdits->getCategoryEditCount(),
213
        ];
214
215
        return $this->getFormattedApiResponse($ret);
216
    }
217
}
218