Passed
Push — master ( 35c320...7deb12 )
by MusikAnimal
04:42
created

TopEditsController   A

Complexity

Total Complexity 23

Size/Duplication

Total Lines 244
Duplicated Lines 0 %

Test Coverage

Coverage 87.5%

Importance

Changes 0
Metric Value
dl 0
loc 244
ccs 7
cts 8
cp 0.875
rs 10
c 0
b 0
f 0
wmc 23

6 Methods

Rating   Name   Duplication   Size   Complexity  
A getIndexRoute() 0 3 1
B singlePageTopEdits() 0 27 3
A resultAction() 0 15 4
B topEditsUserApiAction() 0 55 8
B namespaceTopEdits() 0 48 4
A indexAction() 0 21 3
1
<?php
2
/**
3
 * This file contains only the TopEditsController class.
4
 */
5
6
namespace AppBundle\Controller;
7
8
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
9
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
10
use Symfony\Component\HttpFoundation\Request;
11
use Symfony\Component\HttpFoundation\Response;
12
use Symfony\Component\HttpFoundation\RedirectResponse;
13
use Symfony\Component\HttpFoundation\JsonResponse;
14
use Xtools\Project;
15
use Xtools\User;
16
use Xtools\TopEdits;
17
use Xtools\TopEditsRepository;
18
use Xtools\Edit;
19
20
/**
21
 * The Top Edits tool.
22
 */
23
class TopEditsController extends XtoolsController
24
{
25
26
    /**
27
     * Get the name of the tool's index route.
28
     * This is also the name of the associated model.
29
     * @return string
30
     * @codeCoverageIgnore
31
     */
32
    public function getIndexRoute()
33
    {
34
        return 'TopEdits';
35
    }
36
37
    /**
38
     * Display the form.
39
     * @Route("/topedits", name="topedits")
40
     * @Route("/topedits", name="TopEdits")
41
     * @Route("/topedits/", name="topEditsSlash")
42
     * @Route("/topedits/index.php", name="TopEditsIndex")
43
     * @Route("/topedits/{project}", name="TopEditsProject")
44
     * @param Request $request
45
     * @return Response
46
     */
47 1
    public function indexAction(Request $request)
48
    {
49 1
        $params = $this->parseQueryParams($request);
50
51
        // Redirect if at minimum project and username are provided.
52 1
        if (isset($params['project']) && isset($params['username'])) {
53
            return $this->redirectToRoute('TopEditsResult', $params);
54
        }
55
56
        // Convert the given project (or default project) into a Project instance.
57 1
        $params['project'] = $this->getProjectFromQuery($params);
58
59 1
        return $this->render('topedits/index.html.twig', array_merge([
60 1
            'xtPageTitle' => 'tool-topedits',
61
            'xtSubtitle' => 'tool-topedits-desc',
62
            'xtPage' => 'topedits',
63
64
            // Defaults that will get overriden if in $params.
65
            'namespace' => 0,
66
            'article' => '',
67 1
        ], $params));
68
    }
69
70
    /**
71
     * Display the results.
72
     * @Route("/topedits/{project}/{username}/{namespace}/{article}", name="TopEditsResult",
73
     *     requirements = {"article" = ".+", "namespace" = "|all|\d+"}
74
     * )
75
     * @param Request $request The HTTP request.
76
     * @param int $namespace
77
     * @param string $article
78
     * @return RedirectResponse|Response
79
     * @codeCoverageIgnore
80
     */
81
    public function resultAction(Request $request, $namespace = 0, $article = '')
82
    {
83
        // Second parameter causes it return a Redirect to the index if the user has too many edits.
84
        // We only want to do this when looking at the user's overall edits, not just to a specific article.
85
        $ret = $this->validateProjectAndUser($request, $article !== '' ?  null : 'topedits');
86
        if ($ret instanceof RedirectResponse) {
0 ignored issues
show
introduced by
$ret is always a sub-type of Symfony\Component\HttpFoundation\RedirectResponse.
Loading history...
87
            return $ret;
88
        } else {
89
            list($projectData, $user) = $ret;
90
        }
91
92
        if ($article === '') {
93
            return $this->namespaceTopEdits($request, $user, $projectData, $namespace);
94
        } else {
95
            return $this->singlePageTopEdits($user, $projectData, $namespace, $article);
96
        }
97
    }
98
99
    /**
100
     * List top edits by this user for all pages in a particular namespace.
101
     * @param Request $request The HTTP request.
102
     * @param User $user The User.
103
     * @param Project $project The project.
104
     * @param integer|string $namespace The namespace ID or 'all'
105
     * @return Response
106
     * @codeCoverageIgnore
107
     */
108
    public function namespaceTopEdits(Request $request, User $user, Project $project, $namespace)
109
    {
110
        $isSubRequest = $request->get('htmlonly')
111
            || $this->container->get('request_stack')->getParentRequest() !== null;
112
113
        // Make sure they've opted in to see this data.
114
        if (!$project->userHasOptedIn($user)) {
115
            $optedInPage = $project
116
                ->getRepository()
117
                ->getPage($project, $project->userOptInPage($user));
118
119
            return $this->render('topedits/result_namespace.html.twig', [
120
                'xtPage' => 'topedits',
121
                'xtTitle' => $user->getUsername(),
122
                'project' => $project,
123
                'user' => $user,
124
                'namespace' => $namespace,
125
                'opted_in_page' => $optedInPage,
126
                'is_sub_request' => $isSubRequest,
127
            ]);
128
        }
129
130
        /**
131
         * Max number of rows per namespace to show. `null` here will cause to
132
         * use the TopEdits default.
133
         * @var int
134
         */
135
        $limit = $isSubRequest ? 10 : null;
136
137
        $topEdits = new TopEdits($project, $user, null, $namespace, $limit);
138
        $topEditsRepo = new TopEditsRepository();
139
        $topEditsRepo->setContainer($this->container);
140
        $topEdits->setRepository($topEditsRepo);
141
142
        $topEdits->prepareData();
143
144
        $ret = [
145
            'xtPage' => 'topedits',
146
            'xtTitle' => $user->getUsername(),
147
            'project' => $project,
148
            'user' => $user,
149
            'namespace' => $namespace,
150
            'te' => $topEdits,
151
            'is_sub_request' => $isSubRequest,
152
        ];
153
154
        // Output the relevant format template.
155
        return $this->getFormattedReponse($request, 'topedits/result_namespace', $ret);
156
    }
157
158
    /**
159
     * List top edits by this user for a particular page.
160
     * @param User $user The user.
161
     * @param Project $project The project.
162
     * @param int $namespaceId The ID of the namespace of the page.
163
     * @param string $pageName The title (without namespace) of the page.
164
     * @return RedirectResponse|\Symfony\Component\HttpFoundation\Response
165
     * @codeCoverageIgnore
166
     */
167
    protected function singlePageTopEdits(User $user, Project $project, $namespaceId, $pageName)
168
    {
169
        // Get the full page name (i.e. no namespace prefix if NS 0).
170
        $namespaces = $project->getNamespaces();
171
        $fullPageName = $namespaceId ? $namespaces[$namespaceId].':'.$pageName : $pageName;
172
173
        $page = $this->getAndValidatePage($project, $fullPageName);
174
        if (is_a($page, 'Symfony\Component\HttpFoundation\RedirectResponse')) {
175
            return $page;
176
        }
177
178
        // FIXME: add pagination.
179
        $topEdits = new TopEdits($project, $user, $page);
180
        $topEditsRepo = new TopEditsRepository();
181
        $topEditsRepo->setContainer($this->container);
182
        $topEdits->setRepository($topEditsRepo);
183
184
        $topEdits->prepareData();
185
186
        // Send all to the template.
187
        return $this->render('topedits/result_article.html.twig', [
188
            'xtPage' => 'topedits',
189
            'xtTitle' => $user->getUsername() . ' - ' . $page->getTitle(),
190
            'project' => $project,
191
            'user' => $user,
192
            'page' => $page,
193
            'te' => $topEdits,
194
        ]);
195
    }
196
197
    /************************ API endpoints ************************/
198
199
    /**
200
     * Get the all edits of a user to a specific page, maximum 1000.
201
     * @Route("/api/user/topedits/{project}/{username}/{namespace}/{article}", name="UserApiTopEditsArticle",
202
     *     requirements={"article"=".+", "namespace"="|\d+|all"})
203
     * @Route("/api/user/top_edits/{project}/{username}/{namespace}/{article}", name="UserApiTopEditsArticleUnderscored",
204
     *     requirements={"article"=".+", "namespace"="|\d+|all"})
205
     * @param Request $request
206
     * @param int|string $namespace The ID of the namespace of the page, or 'all' for all namespaces.
207
     * @param string $article The title of the page. A full title can be used if the $namespace is blank.
208
     * @return Response
209
     * TopEdits and its Repo cannot be stubbed here :(
210
     * @codeCoverageIgnore
211
     */
212
    public function topEditsUserApiAction(Request $request, $namespace = 0, $article = '')
213
    {
214
        $this->recordApiUsage('user/topedits');
215
216
        // Second parameter causes it return a Redirect to the index if the user has too many edits.
217
        // We only want to do this when looking at the user's overall edits, not just to a specific article.
218
        $ret = $this->validateProjectAndUser($request, $article !== '' ?  null : 'topedits');
219
        if ($ret instanceof RedirectResponse) {
0 ignored issues
show
introduced by
$ret is always a sub-type of Symfony\Component\HttpFoundation\RedirectResponse.
Loading history...
220
            return $ret;
221
        } else {
222
            list($project, $user) = $ret;
223
        }
224
225
        if (!$project->userHasOptedIn($user)) {
226
            return new JsonResponse(
227
                [
228
                    'error' => 'User:'.$user->getUsername().' has not opted in to detailed statistics.'
229
                ],
230
                Response::HTTP_FORBIDDEN
231
            );
232
        }
233
234
        $limit = $article === '' ? 100 : 1000;
235
        $topEdits = new TopEdits($project, $user, null, $namespace, $limit);
236
        $topEditsRepo = new TopEditsRepository();
237
        $topEditsRepo->setContainer($this->container);
238
        $topEdits->setRepository($topEditsRepo);
239
240
        $response = new JsonResponse();
241
        $response->setEncodingOptions(JSON_NUMERIC_CHECK);
242
243
        if ($article === '') {
244
            // Do format the results.
245
            $topEdits->prepareData();
246
        } else {
247
            $namespaces = $project->getNamespaces();
248
            $fullPageName = is_numeric($namespace) ? $namespaces[$namespace].':'.$article : $article;
249
250
            $page = $this->getAndValidatePage($project, $fullPageName);
251
            if (is_a($page, 'Symfony\Component\HttpFoundation\RedirectResponse')) {
252
                $response->setData([
253
                    'error' => 'Page "'.$article.'" does not exist.',
254
                ]);
255
                $response->setStatusCode(Response::HTTP_NOT_FOUND);
256
                return $response;
257
            }
258
259
            $topEdits->setPage($page);
260
            $topEdits->prepareData(false);
261
        }
262
263
        $response->setData($topEdits->getTopEdits());
264
        $response->setStatusCode(Response::HTTP_OK);
265
266
        return $response;
267
    }
268
}
269