Completed
Push — v0_9 ( 66e765...8c00af )
by Vladimir
03:55
created

HTMLController::getPreviousURL()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 3.0261

Importance

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