Completed
Push — master ( e5ab12...9197f0 )
by Konstantinos
09:09
created

MessageController::setup()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1.037
Metric Value
dl 0
loc 4
ccs 2
cts 3
cp 0.6667
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1.037
1
<?php
2
3
use BZIon\Event\ConversationAbandonEvent;
4
use BZIon\Event\ConversationJoinEvent;
5
use BZIon\Event\ConversationKickEvent;
6
use BZIon\Event\ConversationRenameEvent;
7
use BZIon\Event\Events;
8
use BZIon\Event\NewMessageEvent;
9
use BZIon\Form\Creator\ConversationFormCreator;
10
use BZIon\Form\Creator\ConversationInviteFormCreator;
11
use BZIon\Form\Creator\ConversationRenameFormCreator;
12
use BZIon\Form\Creator\MessageFormCreator;
13
use BZIon\Form\Creator\MessageSearchFormCreator;
14
use BZIon\Search\MessageSearch;
15
use Symfony\Component\Form\Form;
16
use Symfony\Component\HttpFoundation\JsonResponse;
17
use Symfony\Component\HttpFoundation\RedirectResponse;
18
use Symfony\Component\HttpFoundation\Request;
19
20
class MessageController extends JSONController
21
{
22 1
    public function setup()
23
    {
24 1
        $this->requireLogin();
25
    }
26
27
    protected function prepareTwig()
28
    {
29
        $conversations = Conversation::getConversations($this->getMe()->getId());
30
        $this->container->get('twig')->addGlobal("conversations", $conversations);
31
32
        $creator = new MessageSearchFormCreator();
33
        $searchForm = $creator->create();
34
        $this->container->get('twig')->addGlobal("searchForm", $searchForm->createView());
35
    }
36
37
    public function listAction()
38
    {
39
        return array();
40
    }
41
42
    public function composeAction(Player $me, Request $request)
43
    {
44
        if (!$me->hasPermission(Permission::SEND_PRIVATE_MSG)) {
45
            throw new ForbiddenException("You are not allowed to send messages");
46
        }
47
48
        $creator = new ConversationFormCreator($me);
49
        $form = $creator->create()->handleRequest($request);
50
51
        if ($form->isSubmitted()) {
52
            if ($form->isValid()) {
53
                $subject = $form->get('Subject')->getData();
54
                $content = $form->get('Message')->getData();
55
                $recipients = $form->get('Recipients')->getData();
56
57
                $conversation_to = Conversation::createConversation($subject, $me->getId(), $recipients);
58
                $message = $conversation_to->sendMessage($me, $content);
59
60
                $event = new NewMessageEvent($message, true);
61
                $this->dispatch(Events::MESSAGE_NEW, $event);
62
63
                if ($this->isJson()) {
64
                    return new JsonResponse(array(
65
                        'success' => true,
66
                        'message' => 'Your message was sent successfully',
67
                        'id'      => $conversation_to->getId()
68
                    ));
69
                } else {
70
                    return new RedirectResponse($conversation_to->getUrl());
71
                }
72
            } elseif ($this->isJson()) {
73
                throw new BadRequestException($this->getErrorMessage($form));
74
            }
75
        } else {
76
            // Load the list of recipients from the URL
77
            if ($request->query->has('recipients')) {
78
                $form->get('Recipients')->setData($this->decompose(
79
                    $request->query->get('recipients'),
80
                    array('Player', 'Team')
81
                ));
82
            }
83
        }
84
85
        return array("form" => $form->createView());
86
    }
87
88
    public function showAction(Conversation $conversation, Player $me, Request $request)
89
    {
90
        $this->assertCanParticipate($me, $conversation);
91
        $conversation->markReadBy($me->getId());
92
93
        $form = $this->showMessageForm($conversation, $me);
94
        $inviteForm = $this->showInviteForm($conversation, $me);
95
        $renameForm = $this->showRenameForm($conversation, $me);
96
97
        $messages = $this->getQueryBuilder('AbstractMessage')
98
                  ->where('conversation')->is($conversation)
99
                  ->sortBy('time')->reverse()
100
                  ->limit(10)->fromPage($request->query->get('page', 1))
101
                  ->startAt($request->query->get('end'))
102
                  ->endAt($request->query->get('start'))
103
                  ->getModels();
104
105
        $params = array(
106
            "form"         => $form->createView(),
107
            "inviteForm"   => $inviteForm->createView(),
108
            "renameForm"   => $renameForm->createView(),
109
            "conversation" => $conversation,
110
            "messages"     => $messages
111
        );
112
113
        if ($request->query->has('nolayout')) {
114
            // Don't show the layout so that ajax can just load the messages
115
            return $this->render('Message/messages.html.twig', $params);
116
        } else {
117
            return $params;
118
        }
119
    }
120
121
    public function leaveAction(Player $me, Conversation $conversation)
122
    {
123
        if (!$conversation->isMember($me)) {
124
            throw new ForbiddenException("You are not a member of this discussion.");
125
        } elseif ($conversation->getCreator()->isSameAs($me)) {
126
            throw new ForbiddenException("You can't abandon the conversation you started!");
127
        }
128
129
        // TODO: Fix that later
130
        return $this->showConfirmationForm(function () use ($conversation, $me) {
131
            $conversation->removeMember($me);
132
133
            $event = new ConversationAbandonEvent($conversation, $me);
134
            Service::getDispatcher()->dispatch(Events::CONVERSATION_ABANDON, $event);
135
136
            return new RedirectResponse(Service::getGenerator()->generate('message_list'));
137
        },  "Are you sure you want to abandon this conversation?",
138
            "You will no longer receive messages from this conversation", "Leave");
139
    }
140
141
    public function kickAction(Conversation $conversation, Player $player, Player $me)
142
    {
143
        $this->assertCanEdit($me, $conversation, "You are not allowed to kick a player off that discussion!");
144
145
        if ($conversation->isCreator($player->getId())) {
146
            throw new ForbiddenException("You can't leave your own conversation.");
147
        }
148
149
        if (!$conversation->isMember($player)) {
150
            throw new ForbiddenException("The specified player is not a member of this conversation.");
151
        }
152
153
        return $this->showConfirmationForm(function () use ($conversation, $player, $me) {
154
            $conversation->removeMember($player);
155
156
            $event = new ConversationKickEvent($conversation, $player, $me);
157
            Service::getDispatcher()->dispatch(Events::CONVERSATION_KICK, $event);
158
159
            return new RedirectResponse($conversation->getUrl());
160
        },  "Are you sure you want to kick {$player->getEscapedUsername()} from the discussion?",
161
            "Player {$player->getUsername()} has been kicked from the conversation", "Kick");
162
    }
163
164
    public function searchAction(Player $me, Request $request)
165
    {
166
        $query = $request->query->get('q');
167
168
        if (strlen($query) < 3 && !$this->isDebug()) {
169
            // TODO: Find a better error message
170
            throw new BadRequestException('The search term you have provided is too short');
171
        }
172
173
        $search  = new MessageSearch($this->getQueryBuilder(), $me);
0 ignored issues
show
Compatibility introduced by
$this->getQueryBuilder() of type object<QueryBuilder> is not a sub-type of object<MessageQueryBuilder>. It seems like you assume a child class of the class QueryBuilder to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
174
        $results = $search->search($query);
175
176
        return array(
177
            'messages' => $results
178
        );
179
    }
180
181
    /**
182
     * @param Conversation  $conversation
183
     * @param Player $me
184
     *
185
     * @return $this|Form|\Symfony\Component\Form\FormInterface
186
     */
187
    private function showInviteForm($conversation, $me)
188
    {
189
        $creator = new ConversationInviteFormCreator($conversation);
190
        $form = $creator->create()->handleRequest($this->getRequest());
191
192
        if ($form->isValid()) {
193
            $this->assertCanEdit($me, $conversation);
194
            $invitees = array();
195
196
            foreach ($form->get('players')->getData() as $player) {
197
                if (!$conversation->isMember($player)) {
198
                    $conversation->addMember($player);
199
                    $invitees[] = $player;
200
                }
201
            }
202
203
            if (!empty($invitees)) {
204
                $event = new ConversationJoinEvent($conversation, $invitees);
205
                Service::getDispatcher()->dispatch(Events::CONVERSATION_JOIN, $event);
206
            }
207
208
            $this->getFlashBag()->add('success', "The conversation has been updated");
209
210
            // Reset the form fields
211
            return $creator->create();
212
        }
213
214
        return $form;
215
    }
216
217
    /**
218
     * @param Conversation  $conversation
219
     * @param Player $me
220
     */
221
    private function showRenameForm($conversation, $me)
222
    {
223
        $creator = new ConversationRenameFormCreator($conversation);
224
        $form = $creator->create()->handleRequest($this->getRequest());
225
226
        if ($form->isValid()) {
227
            $this->assertCanEdit($me, $conversation);
228
229
            $newName = $form->get('subject')->getData();
230
231
            $event = new ConversationRenameEvent($conversation, $conversation->getSubject(), $newName, $me);
232
            $conversation->setSubject($newName);
233
            Service::getDispatcher()->dispatch(Events::CONVERSATION_RENAME, $event);
234
235
            $this->getFlashBag()->add('success', "The conversation has been updated");
236
        }
237
238
        return $form;
239
    }
240
241
    /**
242
     * @param Conversation  $conversation
243
     * @param Player $me
244
     */
245
    private function showMessageForm($conversation, $me)
246
    {
247
        // Create the form to send a message to the conversation
248
        $creator = new MessageFormCreator($conversation);
249
        $form = $creator->create();
250
251
        // Keep a cloned version so we can come back to it later, if we need
252
        // to reset the fields of the form
253
        $cloned = clone $form;
254
        $form->handleRequest($this->getRequest());
255
256
        if ($form->isValid()) {
257
            // The player wants to send a message
258
            $this->sendMessage($me, $conversation, $form, $cloned);
259
        } elseif ($form->isSubmitted() && $this->isJson()) {
260
            throw new BadRequestException($this->getErrorMessage($form));
261
        }
262
263
        return $form;
264
    }
265
266
    /**
267
     * Make sure that a player can participate in a conversation
268
     *
269
     * Throws an exception if a player is not an admin or a member of that conversation
270
     * @todo Permission for spying on other people's conversations?
271
     * @param  Player        $player  The player to test
272
     * @param  Conversation         $conversation   The message conversation
273
     * @param  string        $message The error message to show
274
     * @throws HTTPException
275
     * @return void
276
     */
277
    private function assertCanParticipate(Player $player, Conversation $conversation,
278
        $message = "You are not allowed to participate in that discussion"
279
    ) {
280
        if (!$conversation->isMember($player)) {
281
            throw new ForbiddenException($message);
282
        }
283
    }
284
285
    /**
286
     * Sends a message to a conversation
287
     *
288
     * @param  Player        $from   The sender
289
     * @param  Conversation         $to     The conversation that will receive the message
290
     * @param  Form          $form   The message's form
291
     * @param  Form          $form   The form before it handled the request
292
     * @param  Form          $cloned
293
     * @throws HTTPException Thrown if the user doesn't have the
294
     *                              SEND_PRIVATE_MSG permission
295
     * @return void
296
     */
297
    private function sendMessage(Player $from, Conversation $to, &$form, $cloned)
298
    {
299
        if (!$from->hasPermission(Permission::SEND_PRIVATE_MSG)) {
300
            throw new ForbiddenException("You are not allowed to send messages");
301
        }
302
303
        $message = $form->get('message')->getData();
304
        $message = $to->sendMessage($from, $message);
305
306
        $this->getFlashBag()->add('success', "Your message was sent successfully");
307
308
        // Let javascript know the message's ID
309
        $this->attributes->set('id', $message->getId());
310
311
        // Reset the form
312
        $form = $cloned;
313
314
        // Notify everyone that we sent a new message
315
        $event = new NewMessageEvent($message, false);
316
        $this->dispatch(Events::MESSAGE_NEW, $event);
317
    }
318
319
    /**
320
     * @return string|null
321
     */
322
    private function getErrorMessage(Form $form)
323
    {
324
        foreach ($form->all() as $child) {
325
            foreach ($child->getErrors() as $error) {
326
                return $error->getMessage();
327
            }
328
        }
329
330
        foreach ($form->getErrors() as $error) {
331
            return $error->getMessage();
332
        }
333
334
        return "Unknown Error";
335
    }
336
337
    /**
338
     * Make sure that a player can edit a conversation
339
     *
340
     * Throws an exception if a player is not an admin or the leader of a team
341
     * @param  Player        $player  The player to test
342
     * @param  Conversation         $conversation   The team
343
     * @param  string        $message The error message to show
344
     * @throws HTTPException
345
     * @return void
346
     */
347
    private function assertCanEdit(Player $player, Conversation $conversation, $message = "You are not allowed to edit the discussion")
348
    {
349
        if ($conversation->getCreator()->getId() != $player->getId()) {
350
            throw new ForbiddenException($message);
351
        }
352
    }
353
}
354