Passed
Push — master ( b63f6f...0c9a5b )
by MusikAnimal
04:18
created

TopEditsController::getToolShortname()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
rs 10
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\RedirectResponse;
11
use Symfony\Component\HttpFoundation\Request;
12
use Symfony\Component\HttpFoundation\Response;
13
use Symfony\Component\HttpFoundation\JsonResponse;
14
use Xtools\Page;
15
use Xtools\PagesRepository;
16
use Xtools\Project;
17
use Xtools\ProjectRepository;
18
use Xtools\User;
19
use Xtools\UserRepository;
20
use Xtools\TopEdits;
21
use Xtools\TopEditsRepository;
22
use Xtools\Edit;
23
24
/**
25
 * The Top Edits tool.
26
 */
27
class TopEditsController extends XtoolsController
28
{
29
30
    /**
31
     * Get the tool's shortname.
32
     * @return string
33
     * @codeCoverageIgnore
34
     */
35
    public function getToolShortname()
36
    {
37
        return 'topedits';
38
    }
39
40
    /**
41
     * Display the form.
42
     * @Route("/topedits", name="topedits")
43
     * @Route("/topedits", name="TopEdits")
44
     * @Route("/topedits/", name="topEditsSlash")
45
     * @Route("/topedits/index.php", name="TopEditsIndex")
46
     * @Route("/topedits/{project}", name="TopEditsProject")
47
     * @param Request $request
48
     * @return Response
49
     */
50 1
    public function indexAction(Request $request)
51
    {
52 1
        $params = $this->parseQueryParams($request);
53
54
        // Redirect if at minimum project and username are provided.
55 1
        if (isset($params['project']) && isset($params['username'])) {
56
            return $this->redirectToRoute('TopEditsResults', $params);
57
        }
58
59
        // Convert the given project (or default project) into a Project instance.
60 1
        $params['project'] = $this->getProjectFromQuery($params);
61
62 1
        return $this->render('topedits/index.html.twig', array_merge([
63 1
            'xtPageTitle' => 'tool-topedits',
64
            'xtSubtitle' => 'tool-topedits-desc',
65
            'xtPage' => 'topedits',
66
67
            // Defaults that will get overriden if in $params.
68
            'namespace' => 0,
69
            'article' => '',
70 1
        ], $params));
71
    }
72
73
    /**
74
     * Display the results.
75
     * @Route("/topedits/{project}/{username}/{namespace}/{article}", name="TopEditsResults",
76
     *     requirements={"article"=".+"})
77
     * @param Request $request The HTTP request.
78
     * @param int $namespace
79
     * @param string $article
80
     * @return RedirectResponse|Response
81
     * @codeCoverageIgnore
82
     */
83
    public function resultAction(Request $request, $namespace = 0, $article = '')
84
    {
85
        // Second parameter causes it return a Redirect to the index if the user has too many edits.
86
        // We only want to do this when looking at the user's overall edits, not just to a specific article.
87
        $ret = $this->validateProjectAndUser($request, $article !== '' ?  null : 'topedits');
88
        if ($ret instanceof RedirectResponse) {
89
            return $ret;
90
        } else {
91
            list($projectData, $user) = $ret;
92
        }
93
94
        if ($article === '') {
95
            return $this->namespaceTopEdits($request, $user, $projectData, $namespace);
96
        } else {
97
            return $this->singlePageTopEdits($user, $projectData, $namespace, $article);
98
        }
99
    }
100
101
    /**
102
     * List top edits by this user for all pages in a particular namespace.
103
     * @param Request $request The HTTP request.
104
     * @param User $user The User.
105
     * @param Project $project The project.
106
     * @param integer|string $namespace The namespace ID or 'all'
107
     * @return \Symfony\Component\HttpFoundation\Response
108
     * @codeCoverageIgnore
109
     */
110
    public function namespaceTopEdits(Request $request, User $user, Project $project, $namespace)
111
    {
112
        $isSubRequest = $request->get('htmlonly')
113
            || $this->container->get('request_stack')->getParentRequest() !== null;
114
115
        // Make sure they've opted in to see this data.
116
        if (!$project->userHasOptedIn($user)) {
117
            $optedInPage = $project
118
                ->getRepository()
119
                ->getPage($project, $project->userOptInPage($user));
1 ignored issue
show
Bug introduced by
The method getPage() does not exist on Xtools\Repository. It seems like you code against a sub-type of Xtools\Repository such as Xtools\ProjectRepository. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

119
                ->/** @scrutinizer ignore-call */ getPage($project, $project->userOptInPage($user));
Loading history...
120
121
            return $this->render('topedits/result_namespace.html.twig', [
122
                'xtPage' => 'topedits',
123
                'xtTitle' => $user->getUsername(),
124
                'project' => $project,
125
                'user' => $user,
126
                'namespace' => $namespace,
127
                'opted_in_page' => $optedInPage,
128
                'is_sub_request' => $isSubRequest,
129
            ]);
130
        }
131
132
        /**
133
         * Max number of rows per namespace to show. `null` here will cause to
134
         * use the TopEdits default.
135
         * @var int
136
         */
137
        $limit = $isSubRequest ? 10 : null;
138
139
        $topEdits = new TopEdits($project, $user, $namespace, $limit);
140
        $topEditsRepo = new TopEditsRepository();
141
        $topEditsRepo->setContainer($this->container);
142
        $topEdits->setRepository($topEditsRepo);
143
144
        return $this->render('topedits/result_namespace.html.twig', [
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
155
    /**
156
     * List top edits by this user for a particular page.
157
     * @param User $user The user.
158
     * @param Project $project The project.
159
     * @param int $namespaceId The ID of the namespace of the page.
160
     * @param string $pageName The title (without namespace) of the page.
161
     * @return RedirectResponse|\Symfony\Component\HttpFoundation\Response
162
     * @codeCoverageIgnore
163
     */
164
    protected function singlePageTopEdits(User $user, Project $project, $namespaceId, $pageName)
165
    {
166
        // Get the full page name (i.e. no namespace prefix if NS 0).
167
        $namespaces = $project->getNamespaces();
168
        $fullPageName = $namespaceId ? $namespaces[$namespaceId].':'.$pageName : $pageName;
169
170
        $page = $this->getAndValidatePage($project, $fullPageName);
171
        if (is_a($page, 'Symfony\Component\HttpFoundation\RedirectResponse')) {
172
            return $page;
173
        }
174
175
        // Get all revisions of this page by this user.
176
        $revisionsData = $page->getRevisions($user);
177
178
        // Loop through all revisions and format dates, find totals, etc.
179
        $totalAdded = 0;
180
        $totalRemoved = 0;
181
        $revisions = [];
182
        foreach ($revisionsData as $revision) {
183
            if ($revision['length_change'] > 0) {
184
                $totalAdded += $revision['length_change'];
185
            } else {
186
                $totalRemoved += $revision['length_change'];
187
            }
188
            $revisions[] = new Edit($page, $revision);
189
        }
190
191
        // Send all to the template.
192
        return $this->render('topedits/result_article.html.twig', [
193
            'xtPage' => 'topedits',
194
            'xtTitle' => $user->getUsername() . ' - ' . $page->getTitle(),
195
            'project' => $project,
196
            'user' => $user,
197
            'page' => $page,
198
            'total_added' => $totalAdded,
199
            'total_removed' => $totalRemoved,
200
            'revisions' => $revisions,
201
            'revision_count' => count($revisions),
202
        ]);
203
    }
204
205
    /************************ API endpoints ************************/
206
207
    /**
208
     * Get the all edits of a user to a specific page, maximum 1000.
209
     * @Route("/api/user/topedits/{project}/{username}/{namespace}/{article}", name="UserApiTopEditsArticle",
210
     *     requirements={"article"=".+", "namespace"="|\d+|all"})
211
     * @Route("/api/user/top_edits/{project}/{username}/{namespace}/{article}", name="UserApiTopEditsArticleUnderscored",
212
     *     requirements={"article"=".+", "namespace"="|\d+|all"})
213
     * @param Request $request
214
     * @param int|all $namespace The ID of the namespace of the page, or 'all' for all namespaces.
0 ignored issues
show
Bug introduced by
The type AppBundle\Controller\all was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
215
     * @param string $article The title of the page. A full title can be used if the $namespace is blank.
216
     * @return Response
217
     * TopEdits and its Repo cannot be stubbed here :(
218
     * @codeCoverageIgnore
219
     */
220
    public function topEditsUserApiAction(Request $request, $namespace = 0, $article = '')
221
    {
222
        // Second parameter causes it return a Redirect to the index if the user has too many edits.
223
        // We only want to do this when looking at the user's overall edits, not just to a specific article.
224
        $ret = $this->validateProjectAndUser($request, $article !== '' ?  null : 'topedits');
225
        if ($ret instanceof RedirectResponse) {
226
            return $ret;
227
        } else {
228
            list($project, $user) = $ret;
229
        }
230
231
        if (!$project->userHasOptedIn($user)) {
232
            return new JsonResponse(
233
                [
234
                    'error' => 'User:'.$user->getUsername().' has not opted in to detailed statistics.'
235
                ],
236
                Response::HTTP_FORBIDDEN
237
            );
238
        }
239
240
        $limit = $article === '' ? 100 : 1000;
241
        $topEdits = new TopEdits($project, $user, $namespace, $limit);
242
        $topEditsRepo = new TopEditsRepository();
243
        $topEditsRepo->setContainer($this->container);
244
        $topEdits->setRepository($topEditsRepo);
245
246
        $response = new JsonResponse();
247
        $response->setEncodingOptions(JSON_NUMERIC_CHECK);
248
249
        if ($article === '') {
250
            $data = $topEdits->getTopEdits();
251
252
            if (is_numeric($namespace)) {
253
                $data = $data[$namespace];
254
            }
255
        } else {
256
            $namespaces = $project->getNamespaces();
257
            $fullPageName = is_numeric($namespace) ? $namespaces[$namespace].':'.$article : $article;
258
259
            $page = $this->getAndValidatePage($project, $fullPageName);
260
            if (is_a($page, 'Symfony\Component\HttpFoundation\RedirectResponse')) {
261
                $data = [
262
                    'error' => 'Page "'.$article.'" does not exist.',
263
                ];
264
                $response->setStatusCode(Response::HTTP_NOT_FOUND);
265
            } else {
266
                // Database sorts by timestamp ascending, and here we want it descending.
267
                $data = array_reverse($page->getRevisions($user));
268
                $response->setStatusCode(Response::HTTP_OK);
269
            }
270
        }
271
272
        $response->setData($data);
273
        return $response;
274
    }
275
}
276