QuoteController   A
last analyzed

Complexity

Total Complexity 19

Size/Duplication

Total Lines 246
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 71
dl 0
loc 246
rs 10
c 0
b 0
f 0
wmc 19

9 Methods

Rating   Name   Duplication   Size   Complexity  
A randomQuoteAction() 0 12 2
A indexAction() 0 18 2
A quoteAllAction() 0 18 2
A getIndexRoute() 0 3 1
A quoteAction() 0 29 4
A validateIsEnabled() 0 5 3
A randomQuoteApiAction() 0 11 1
A singleQuotesApiAction() 0 22 2
A allQuotesApiAction() 0 14 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace App\Controller;
6
7
use OpenApi\Annotations as OA;
8
use Symfony\Component\HttpFoundation\JsonResponse;
9
use Symfony\Component\HttpFoundation\RedirectResponse;
10
use Symfony\Component\HttpFoundation\Response;
11
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
12
use Symfony\Component\Routing\Annotation\Route;
13
use Symfony\Component\Routing\Exception\InvalidParameterException;
14
15
/**
16
 * A quick note: This tool is referred to as "bash" in much of the legacy code base.  As such,
17
 * the terms "quote" and "bash" are used interchangeably here, so as to not break many conventions.
18
 *
19
 * This tool is intentionally disabled in the WMF installation.
20
 * @codeCoverageIgnore
21
 */
22
class QuoteController extends XtoolsController
23
{
24
25
    /**
26
     * @inheritDoc
27
     * @codeCoverageIgnore
28
     */
29
    public function getIndexRoute(): string
30
    {
31
        return 'Quote';
32
    }
33
34
    /**
35
     * Method for rendering the Bash Main Form. This method redirects if valid parameters are found,
36
     * making it a valid form endpoint as well.
37
     * @Route("/bash", name="Bash")
38
     * @Route("/quote", name="Quote")
39
     * @Route("/bash/base.php", name="BashBase")
40
     * @return Response
41
     */
42
    public function indexAction(): Response
43
    {
44
        // Check to see if the quote is a param.  If so,
45
        // redirect to the proper route.
46
        if ('' != $this->request->query->get('id')) {
47
            return $this->redirectToRoute(
48
                'QuoteID',
49
                ['id' => $this->request->query->get('id')]
50
            );
51
        }
52
53
        // Otherwise render the form.
54
        return $this->render(
55
            'quote/index.html.twig',
56
            [
57
                'xtPage' => 'Quote',
58
                'xtPageTitle' => 'tool-bash',
59
                'xtSubtitle' => 'tool-bash-desc',
60
            ]
61
        );
62
    }
63
64
    /**
65
     * Method for rendering a random quote. This should redirect to the /quote/{id} path below.
66
     * @Route("/quote/random", name="QuoteRandom")
67
     * @Route("/bash/random", name="BashRandom")
68
     * @return RedirectResponse
69
     */
70
    public function randomQuoteAction(): RedirectResponse
71
    {
72
        // Choose a random quote by ID. If we can't find the quotes, return back to
73
        // the main form with a flash notice.
74
        try {
75
            $id = rand(1, sizeof($this->getParameter('quotes')));
0 ignored issues
show
Bug introduced by
It seems like $this->getParameter('quotes') can also be of type boolean and double and integer and null and string; however, parameter $value of sizeof() does only seem to accept Countable|array, 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

75
            $id = rand(1, sizeof(/** @scrutinizer ignore-type */ $this->getParameter('quotes')));
Loading history...
76
        } catch (InvalidParameterException $e) {
77
            $this->addFlashMessage('notice', 'noquotes');
78
            return $this->redirectToRoute('Quote');
79
        }
80
81
        return $this->redirectToRoute('QuoteID', ['id' => $id]);
82
    }
83
84
    /**
85
     * Method to show all quotes.
86
     * @Route("/quote/all", name="QuoteAll")
87
     * @Route("/bash/all", name="BashAll")
88
     * @return Response
89
     */
90
    public function quoteAllAction(): Response
91
    {
92
        // Load up an array of all the quotes.
93
        // if we can't find the quotes, return back to  the main form with
94
        // a flash notice.
95
        try {
96
            $quotes = $this->getParameter('quotes');
97
        } catch (InvalidParameterException $e) {
98
            $this->addFlashMessage('notice', 'noquotes');
99
            return $this->redirectToRoute('Quote');
100
        }
101
102
        // Render the page.
103
        return $this->render(
104
            'quote/all.html.twig',
105
            [
106
                'xtPage' => 'Quote',
107
                'quotes' => $quotes,
108
            ]
109
        );
110
    }
111
112
    /**
113
     * Method to render a single quote.
114
     * @param int $id ID of the quote
115
     * @Route("/quote/{id}", name="QuoteID")
116
     * @Route("/bash/{id}", name="BashID")
117
     * @return Response
118
     */
119
    public function quoteAction(int $id): Response
120
    {
121
        // Get the singular quote.
122
        // If we can't find the quotes, return back to  the main form with a flash notice.
123
        try {
124
            if (isset($this->getParameter('quotes')[$id])) {
125
                $text = $this->getParameter('quotes')[$id];
126
            } else {
127
                throw new InvalidParameterException("Quote doesn't exist'");
128
            }
129
        } catch (InvalidParameterException $e) {
130
            $this->addFlashMessage('notice', 'noquotes');
131
            return $this->redirectToRoute('Quote');
132
        }
133
134
        // If the text is undefined, that quote doesn't exist.
135
        // Redirect back to the main form.
136
        if (!isset($text)) {
137
            $this->addFlashMessage('notice', 'noquotes');
138
            return $this->redirectToRoute('Quote');
139
        }
140
141
        // Show the quote.
142
        return $this->render(
143
            'quote/view.html.twig',
144
            [
145
                'xtPage' => 'Quote',
146
                'text' => $text,
147
                'id' => $id,
148
            ]
149
        );
150
    }
151
152
    /************************ API endpoints ************************/
153
154
    /**
155
     * Get random quote.
156
     * @Route("/api/quote/random", name="QuoteApiRandom", methods={"GET"})
157
     * @OA\Tag(name="Quote API")
158
     * @OA\Get(description="Get a random quote. The quotes are sourced from [developer quips](https://w.wiki/6rpo)
159
           and [IRC quotes](https://meta.wikimedia.org/wiki/IRC/Quotes/archives).")
160
     * @OA\Response(
161
     *     response=200,
162
     *     description="Quote keyed by ID.",
163
     *     @OA\JsonContent(
164
     *         @OA\Property(property="<quote-id>", type="string")
165
     *     )
166
     * )
167
     * @return JsonResponse
168
     * @codeCoverageIgnore
169
     */
170
    public function randomQuoteApiAction(): JsonResponse
171
    {
172
        $this->validateIsEnabled();
173
174
        $this->recordApiUsage('quote/random');
175
        $quotes = $this->getParameter('quotes');
176
        $id = array_rand($quotes);
177
178
        return new JsonResponse(
179
            [$id => $quotes[$id]],
180
            Response::HTTP_OK
181
        );
182
    }
183
184
    /**
185
     * Get all quotes.
186
     * @Route("/api/quote/all", name="QuoteApiAll", methods={"GET"})
187
     * @OA\Tag(name="Quote API")
188
     * @OA\Get(description="Get a list of all quotes, sourced from [developer quips](https://w.wiki/6rpo)
189
           and [IRC quotes](https://meta.wikimedia.org/wiki/IRC/Quotes/archives).")
190
     * @OA\Response(
191
     *     response=200,
192
     *     description="All quotes, keyed by ID.",
193
     *     @OA\JsonContent(
194
     *         @OA\Property(property="<quote-id>", type="string")
195
     *     )
196
     * )
197
     * @return Response
198
     * @codeCoverageIgnore
199
     */
200
    public function allQuotesApiAction(): Response
201
    {
202
        $this->validateIsEnabled();
203
204
        $this->recordApiUsage('quote/all');
205
        $quotes = $this->getParameter('quotes');
206
        $numberedQuotes = [];
207
208
        // Number the quotes, since they somehow have significance.
209
        foreach ($quotes as $index => $quote) {
210
            $numberedQuotes[(string)($index + 1)] = $quote;
211
        }
212
213
        return new JsonResponse($numberedQuotes, Response::HTTP_OK);
214
    }
215
216
    /**
217
     * Get the quote with the given ID.
218
     * @Route("/api/quote/{id}", name="QuoteApiQuote", requirements={"id"="\d+"}, methods={"GET"})
219
     * @OA\Tag(name="Quote API")
220
     * @OA\Get(description="Get a quote with the given ID.")
221
     * @OA\Parameter(name="id", in="path", required="true", @OA\Schema(type="integer", minimum=0))
222
     * @OA\Response(
223
     *     response=200,
224
     *     description="Quote keyed by ID.",
225
     *     @OA\JsonContent(
226
     *         @OA\Property(property="<quote-id>", type="string")
227
     *     )
228
     * )
229
     * @param int $id
230
     * @return JsonResponse
231
     * @codeCoverageIgnore
232
     */
233
    public function singleQuotesApiAction(int $id): JsonResponse
234
    {
235
        $this->validateIsEnabled();
236
237
        $this->recordApiUsage('quote/id');
238
        $quotes = $this->getParameter('quotes');
239
240
        if (!isset($quotes[$id])) {
241
            return new JsonResponse(
242
                [
243
                    'error' => [
244
                        'code' => Response::HTTP_NOT_FOUND,
245
                        'message' => 'No quote found with ID '.$id,
246
                    ],
247
                ],
248
                Response::HTTP_NOT_FOUND
249
            );
250
        }
251
252
        return new JsonResponse([
253
            $id => $quotes[$id],
254
        ], Response::HTTP_OK);
255
    }
256
257
    /**
258
     * Validate that the Quote tool is enabled, and throw a 404 if it is not.
259
     * This is normally done by DisabledToolSubscriber but we have special logic here, because for Labs we want to
260
     * show the quote in the footer but not expose the web interface.
261
     * @throws NotFoundHttpException
262
     */
263
    private function validateIsEnabled(): void
264
    {
265
        $isLabs = $this->getParameter('app.is_wmf');
266
        if (!$isLabs && !$this->getParameter('enable.Quote')) {
267
            throw $this->createNotFoundException('This tool is disabled');
268
        }
269
    }
270
}
271