HTMLController::getPreviousURL()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 3.072

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 4
cts 5
cp 0.8
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 7
nc 3
nop 0
crap 3.072
1
<?php
2
3
use BZIon\Form\Creator\ConfirmationFormCreator;
4
use BZIon\Twig\AppGlobal;
5
use BZIon\Twig\ModelFetcher;
6
use Symfony\Component\HttpFoundation\RedirectResponse;
7
8
/**
9
 * @package BZiON\Controllers
10
 */
11
abstract class HTMLController extends Controller
12
{
13
    /**
14
     * Whether twig has been prepared
15
     * @var bool
16
     */
17
    public $twigReady = false;
18
19
    /**
20
     * Prepare the twig global variables
21
     */
22 1
    private function addTwigGlobals()
23
    {
24 1
        if ($this->twigReady) {
25
            return;
26
        }
27
28 1
        $request = $this->getRequest();
29
30
        // Add global variables to the twig templates
31 1
        $twig = $this->container->get('twig');
32 1
        $twig->addGlobal("me",      $this->getMe());
33 1
        $twig->addGlobal("model",   new ModelFetcher());
34 1
        $twig->addGlobal("request", $request);
35 1
        $twig->addGlobal("session", $request->getSession());
36
37 1
        $twig->addGlobal("app", new AppGlobal($this->parent, $this->container));
38
39 1
        $this->prepareTwig();
40
41 1
        $this->twigReady = true;
42 1
    }
43
44 1
    protected function prepareTwig()
45
    {
46 1
    }
47
48
    /**
49
     * {@inheritdoc}
50
     * @param string $view
51
     */
52 1
    protected function render($view, $parameters = array())
53
    {
54 1
        $this->addTwigGlobals();
55
56 1
        return parent::render($view, $parameters);
57
    }
58
59
    /**
60
     * {@inheritdoc}
61
     *
62
     * @throws ModelNotFoundException
63
     */
64 1
    protected function findModelInParameters($modelParameter, $routeParameters)
65
    {
66 1
        $model = parent::findModelInParameters($modelParameter, $routeParameters);
67
68 1
        // Special action to restore objects meaning we should always be able to see the model we're working with.
69
        $restoring = ($routeParameters->get('_action') === 'restore');
70
71
        if (!$model instanceof Model || $modelParameter->getName() === "me") {
72 1
            // `$me` can be invalid if, for example, no user is currently logged
73 1
            // in - in this case we can just pass the invalid Player model to
74
            // the controller without complaining
75
            return $model;
76 1
        } elseif (!$this->canSee($model, $restoring)) {
77
            // If the model is not supposed to be visible to the player
78
            // requesting it, pretend it's not there
79 1
            throw new ModelNotFoundException($model->getTypeForHumans());
80
        }
81
82
        return $model;
83
    }
84
85 1
    /**
86
     * {@inheritdoc}
87 1
     */
88 1
    public function callAction($action = null)
89 1
    {
90 1
        $response = parent::callAction($action);
91 1
        if (!$response->isRedirection()
92
            && !$response->isNotFound()
93
            && !$this->getRequest()->isXmlHttpRequest()) {
94 1
            $this->saveURL();
95
        }
96
97
        return $response;
98
    }
99
100
    /**
101 1
     * Save the URL of the current page so that the user can be redirected back to it
102
     * if they login
103 1
     */
104
    protected function saveURL()
105 1
    {
106 1
        $session = $this->getRequest()->getSession();
107
108
        $urls = $session->get('previous_paths', array());
109 1
        array_unshift($urls, $this->getRequest()->getPathInfo());
110 1
111
        // No need to have more than 4 urls stored on the array
112
        while (count($urls) > 4) {
113
            array_pop($urls);
114 1
        }
115 1
116
        // Store the URLs in the session, removing any duplicate entries
117
        $session->set('previous_paths', array_unique($urls));
118
    }
119
120
    /**
121 1
     * Returns the path to the home page
122
     * @return string
123 1
     */
124
    protected function getHomeURL()
125
    {
126
        return Service::getGenerator()->generate('index');
127
    }
128
129
    /**
130 1
     * Returns the URL of the previous page
131
     * @return string
132 1
     */
133
    protected function getPreviousURL()
134 1
    {
135 1
        $request = $this->getRequest();
136 1
137
        $urls = $request->getSession()->get('previous_paths', array());
138 1
        foreach ($urls as $url) {
139
            if ($url != $request->getPathInfo()) {
140
                // Don't redirect to the same page
141
                return $request->getBaseUrl() . $url;
142
            }
143
        }
144
145
        // No stored URLs found, just redirect them to the home page
146
        return $this->getHomeURL();
147
    }
148
149
    /**
150
     * Returns a redirect response to the previous page
151
     * @return RedirectResponse
152
     */
153
    protected function goBack()
154
    {
155
        return new RedirectResponse($this->getPreviousURL());
156
    }
157
158
    /**
159 1
     * Returns a redirect response to the home page
160
     * @return RedirectResponse
161 1
     */
162
    protected function goHome()
163
    {
164
        return new RedirectResponse($this->getHomeURL());
165
    }
166
167
    /**
168 1
     * Get the current pagination value
169
     *
170 1
     * @return int Always a positive value greater than 0
171
     */
172
    protected function getCurrentPage()
173
    {
174
        $page = intval(self::getRequest()->get('page', 1));
175
176
        return (($page <= 0) ? 1 : $page);
177
    }
178
179
    /**
180
     * Get the session's flash bag
181
     * @return Symfony\Component\HttpFoundation\Session\Flash\FlashBag
182
     */
183 1
    public static function getFlashBag()
184
    {
185 1
        return self::getRequest()->getSession()->getFlashBag();
186
    }
187
188
    /**
189 1
     * Find out whether the currently logged in user can see a model
190
     *
191
     * Apart from the permissions of the user, this method takes the request
192
     * query into consideration to find out if the user wants to see deleted
193
     * models or not.
194
     *
195
     * @param  Model $model     Model in question
196
     * @param  bool  $restoring If we're attempting to restore the object, we should always be able to see it
197
     * @return bool
198 1
     */
199
    public static function canSee($model, $restoring = false)
200
    {
201 1
        if (!$model instanceof PermissionModel) {
202 1
            return !$model->isDeleted();
203
        }
204 1
205
        return static::getMe()->canSee($model, (static::getRequest()->get('showDeleted') || $restoring));
206
    }
207
208
    /**
209
     * Assert that the user is logged in
210
     * @param  string        $message The message to show if the user is not logged in
211
     * @throws HTTPException
212
     * @return void
213
     */
214
    protected function requireLogin(
215
        $message = "You need to be signed in to do this"
216
    ) {
217
        if (!$this->getMe()->isValid()) {
218
            throw new ForbiddenException($message);
219
        }
220
    }
221
222
    /**
223
     * Show a confirmation (Yes, No) form to the user
224
     *
225 1
     * @param  callable $onYes          What to do if the user clicks on "Yes"
226
     * @param  string   $message        The message to show to the user, asking them to confirm their action (RAW text
227
     *                                  is shown - don't forget to properly escape your parameters!)
228
     * @param  string   $action         The text to show on the "Yes" button
229
     * @param  string   $successMessage A message to add on the session's flashbag on success (flashbags automatically escape text)
230
     * @param  callable $onNo           What to do if the user presses "No" - defaults to redirecting them back
231
     * @param  string   $view           The view to redirect to
232
     * @param  bool     $noButton       Whether to show a "No" instead of a "Cancel" button
233
     * @param  bool     $destructive    Whether or not to mark the action button as being a destructive operation
234 1
     *
235 1
     * @return mixed    The response
236
     */
237 1
    protected function showConfirmationForm(
238 1
        $onYes,
239 1
        $message = "Are you sure you want to do this?",
240
        $successMessage = "Operation completed successfully",
241
        $action = "Yes",
242 1
        $onNo = null,
243
        $view = 'confirmation.html.twig',
244 1
        $noButton = false,
245 1
        $destructive = false
246
    ) {
247
        $creator = new ConfirmationFormCreator($action, $this->getPreviousURL(), $noButton, $destructive);
248 1
        $form = $creator->create()->handleRequest($this->getRequest());
249
250
        if ($form->isValid()) {
251
            if ($form->get('confirm')->isClicked()) {
252
                $return = $onYes();
253
254 1
                // If no exceptions are thrown, show a success message
255 1
                $this->getFlashBag()->add('success', $successMessage);
256 1
257
                return $return;
258
            } elseif (!$onNo) {
259
                // We didn't get told about what to do when the user presses no,
260
                // just get them back where they were
261
                return new RedirectResponse($form->get('original_url')->getData());
262
            } else {
263
                return $onNo($form->get('original_url')->getData());
264
            }
265
        }
266
267
        return $this->render($view, array(
268
            'form'    => $form->createView(),
269
            'message' => $message
270
        ));
271
    }
272
273
    /**
274
     * Decompose a list of object IDs into the corresponding IDs
275
     *
276
     * @param string $query  The user's query
277
     * @param array  $types  A list of the acceptable model types (will NOT be sanitized)
278
     * @param bool   $models Whether to return an array of models instead of an array of IDs
279
     * @param int|null $max  The largest number of models to accept, or null for infinite models
280
     *
281
     * @throws BadRequestException
282
     * @return int[]|Model[] An array of models or IDs (depending on the $models parameter)
283
     */
284
    protected function decompose($query, array $types, $models = true, $max = null)
285
    {
286
        $query = explode(',', $query);
287
288
        if ($max !== null && count($query) > $max) {
289
            throw new \BadRequestException("Too many objects provided");
290
        }
291
292
        $result = array();
293
        $firstType = reset($types);
294
295
        if (!$models) {
296
            // Make sure the result array has a subarray for each type
297
            foreach ($types as $type) {
298
                $result[$type] = array();
299
            }
300
        }
301
302
        foreach ($query as $object) {
303
            if ($object === '') {
304
                continue;
305
            }
306
307
            $object = explode(':', $object, 3);
308
            if (count($object) === 2) {
309
                $class = ucfirst($object[0]);
310
                $id = (int) $object[1];
311
312
                if (!in_array($class, $types)) {
313
                    throw new \BadRequestException("Invalid object type");
314
                }
315
316
                if ($models) {
317
                    $this->assertVisibility($result[] = $class::get($id));
318
                } else {
319
                    $result[$class][] = $id;
320
                }
321
            } elseif (count($object) === 1) {
322
                // No type was provided
323
                if (count('types') > 1) {
324
                    throw new \BadRequestException(
325
                        "You need to provide the type of the object"
326
                    );
327
                }
328
329
                if ($models) {
330
                    $this->assertVisibility($result[] = $firstType::get($object[0]));
331
                } else {
332
                    $result[$firstType][] = (int) $object[0];
333
                }
334
            } else {
335
                throw new \BadRequestException("Malformed object");
336
            }
337
        }
338
339
        return $result;
340
    }
341
342
    /**
343
     * Throw an innocent exception if a player can't see a Model or if it
344
     * doesn't exist
345
     *
346
     * @param PermissionModel $model The model to test
347
     *
348
     * @throws BadRequestException
349
     */
350
    private function assertVisibility(PermissionModel $model)
351
    {
352
        if (!$this->getMe()->canSee($model)) {
353
            throw new \BadRequestException("Invalid object provided");
354
        }
355
    }
356
}
357