TaskStackManager::setTaskSkipped()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 2

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 2
dl 0
loc 9
ccs 6
cts 6
cp 1
crap 2
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * This file is part of the login-cidadao project or it's bundles.
4
 *
5
 * (c) Guilherme Donato <guilhermednt on github>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace LoginCidadao\TaskStackBundle\Service;
12
13
use FOS\OAuthServerBundle\Security\Authentication\Token\OAuthToken;
14
use LoginCidadao\CoreBundle\Model\PersonInterface;
15
use LoginCidadao\TaskStackBundle\Event\GetTasksEvent;
16
use LoginCidadao\TaskStackBundle\Exception\UnsupportedTargetException;
17
use LoginCidadao\TaskStackBundle\Model\IntentTask;
18
use LoginCidadao\TaskStackBundle\Model\RouteTaskTarget;
19
use LoginCidadao\TaskStackBundle\Model\TaskInterface;
20
use LoginCidadao\TaskStackBundle\Model\TaskStack;
21
use LoginCidadao\TaskStackBundle\Model\TaskTargetInterface;
22
use LoginCidadao\TaskStackBundle\Model\UrlTaskTarget;
23
use LoginCidadao\TaskStackBundle\TaskStackEvents;
24
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
25
use Symfony\Component\HttpFoundation\RedirectResponse;
26
use Symfony\Component\HttpFoundation\Request;
27
use Symfony\Component\HttpFoundation\Response;
28
use Symfony\Component\HttpFoundation\Session\SessionInterface;
29
use Symfony\Component\Routing\RouterInterface;
30
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
31
32
class TaskStackManager implements TaskStackManagerInterface
33
{
34
    const SKIPPED_TASKS_KEY = 'tasks.skipped';
35
    const TASKS_STACK_KEY = 'tasks.stack';
36
37
    /** @var SessionInterface */
38
    private $session;
39
40
    /** @var TokenStorageInterface */
41
    private $tokenStorage;
42
43
    /** @var RouterInterface */
44
    private $router;
45
46
    /** @var EventDispatcherInterface */
47
    private $dispatcher;
48
49
    /**
50
     * TaskStackManager constructor.
51
     * @param SessionInterface $session
52
     * @param TokenStorageInterface $tokenStorage
53
     * @param RouterInterface $router
54
     * @param EventDispatcherInterface $dispatcher
55
     */
56 15
    public function __construct(
57
        SessionInterface $session,
58
        TokenStorageInterface $tokenStorage,
59
        RouterInterface $router,
60
        EventDispatcherInterface $dispatcher
61
    ) {
62 15
        $this->session = $session;
63 15
        $this->tokenStorage = $tokenStorage;
64 15
        $this->router = $router;
65 15
        $this->dispatcher = $dispatcher;
66 15
    }
67
68
    /**
69
     * @return TaskStack
70
     */
71 12
    private function getStack()
72
    {
73
        /** @var TaskStack $stack */
74 12
        $stack = $this->session->get(self::TASKS_STACK_KEY, new TaskStack());
75
76 12
        return $stack;
77
    }
78
79
    /**
80
     * @return array
81
     */
82 11
    private function getSkippedTasks()
83
    {
84 11
        return $this->session->get(self::SKIPPED_TASKS_KEY, []);
85
    }
86
87
    /**
88
     * Stacks the given $task, making sure there are no duplicates and that it was not skipped.
89
     *
90
     * @param TaskInterface $task
91
     * @param IntentTask|null $intentTask
92
     * @return TaskStackManagerInterface
93
     */
94 11
    public function addNotSkippedTaskOnce(TaskInterface $task, IntentTask $intentTask = null)
95
    {
96 11
        $stack = $this->getStack();
97 11
        $isSkipped = !$task->isMandatory() && $this->isSkipped($task);
98 11
        if ($isSkipped === false && $stack->hasTask($task) === false) {
99 11
            if ($intentTask) {
100 1
                $this->addNotSkippedTaskOnce($intentTask);
101 1
                $stack = $this->getStack();
102
            }
103 11
            $stack->push($task);
104 11
            $this->updateStack($stack);
105
        }
106
107 11
        return $this;
108
    }
109
110
    /**
111
     * @param TaskInterface $task
112
     * @param bool $isSkipped
113
     * @return TaskStackManagerInterface|void
114
     */
115 2
    public function setTaskSkipped(TaskInterface $task, $isSkipped = true)
116
    {
117 2
        $skipped = $this->getSkippedTasks();
118 2
        if ($isSkipped) {
119 2
            $skipped[$task->getId()] = true;
120
        } else {
121 1
            unset($skipped[$task->getId()]);
122
        }
123 2
        $this->updateSkipped($skipped);
124 2
    }
125
126
    /**
127
     * @param Request $request
128
     * @param Response|null $defaultResponse
129
     * @return Response
130
     */
131 8
    public function processRequest(Request $request, Response $defaultResponse = null)
132
    {
133 8
        $token = $this->tokenStorage->getToken();
134 8
        if (!$token || $token instanceof OAuthToken || !$token->getUser() instanceof PersonInterface) {
135 2
            return $defaultResponse;
136
        }
137
138 6
        $this->populateStack($request);
139
140 6
        $stack = $this->getStack();
141
142 6
        if ($stack->isEmpty()) {
143 1
            return $defaultResponse;
144
        }
145
146
        /** @var TaskInterface $task */
147 5
        $task = $stack->top();
148
149
        // If it's an IntentTask, it's just a redirect, so we have to consume it ourselves.
150 5
        if ($task instanceof IntentTask) {
151 1
            $stack->pop();
152 1
            $this->updateStack($stack);
153
        }
154
155 5
        if ($this->isSkipped($task)) {
156 1
            $stack->pop();
157 1
            $this->updateStack($stack);
158
159 1
            return $defaultResponse;
160
        }
161
162 4
        if (false === $task->isTaskRoute($request->attributes->get('_route'))) {
163 3
            $target = $task->getTarget();
164 3
            $url = $this->getTargetUrl($target);
165
166 2
            return new RedirectResponse($url);
167
        }
168
169 1
        return $defaultResponse;
170
    }
171
172 1
    public function emptyStack()
173
    {
174 1
        $this->updateStack(new TaskStack());
175 1
    }
176
177
    /**
178
     * @param Request $request
179
     */
180 6
    private function populateStack(Request $request)
181
    {
182 6
        $event = new GetTasksEvent($this, $request);
183 6
        $this->dispatcher->dispatch(TaskStackEvents::GET_TASKS, $event);
184 6
    }
185
186
    /**
187
     * @param TaskInterface $task
188
     * @return bool
189
     */
190 10
    private function isSkipped(TaskInterface $task)
191
    {
192 10
        return false !== array_key_exists($task->getId(), $this->getSkippedTasks());
193
    }
194
195 12
    private function updateStack(TaskStack $stack)
196
    {
197 12
        $this->session->set(self::TASKS_STACK_KEY, $stack);
198 12
    }
199
200 2
    private function updateSkipped(array $skippedTasks)
201
    {
202 2
        $this->session->set(self::SKIPPED_TASKS_KEY, $skippedTasks);
203 2
    }
204
205
    /**
206
     * @return TaskInterface|null
207
     */
208 1
    public function getCurrentTask()
209
    {
210 1
        $stack = $this->getStack();
211
212
        /** @var TaskInterface|null $task */
213 1
        $task = $stack->isEmpty() ? null : $stack->top();
214
215 1
        return $task;
216
    }
217
218 1
    public function getNextTask()
219
    {
220 1
        $stack = $this->getStack();
221 1
        $peeked = 0;
222 1
        foreach ($stack as $task) {
223 1
            if ($peeked < 1) {
224 1
                $peeked++;
225 1
                continue;
226
            }
227
228 1
            return $task;
229
        }
230
231 1
        return null;
232
    }
233
234
    /**
235
     * Counts how many Tasks are in the Stack
236
     *
237
     * @return int
238
     */
239 2
    public function countTasks()
240
    {
241 2
        return $this->getStack()->count();
242
    }
243
244
    /**
245
     * @return bool
246
     */
247 1
    public function hasIntentTask()
248
    {
249 1
        return $this->getStack()->hasIntentTask();
250
    }
251
252
    /**
253
     * Converts a TaskTargetInterface into a string URL.
254
     *
255
     * @param TaskTargetInterface $target
256
     * @return string
257
     */
258 3
    public function getTargetUrl(TaskTargetInterface $target)
259
    {
260 3
        if ($target instanceof RouteTaskTarget) {
261 1
            $url = $this->router->generate($target->getRoute(), $target->getParameters());
262 2
        } elseif ($target instanceof UrlTaskTarget) {
263 1
            $url = $target->getUrl();
264
        } else {
265 1
            throw new UnsupportedTargetException($target);
266
        }
267
268 2
        return $url;
269
    }
270
}
271