Passed
Push — master ( 9a481f...a0a42c )
by MusikAnimal
05:51
created

TopEditsController::singlePageTopEdits()   B

Complexity

Conditions 3
Paths 4

Size

Total Lines 27
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

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