Completed
Branch dev (276354)
by Raffael
15:43
created

Notifications::postSubscribtions()   B

Complexity

Conditions 6
Paths 7

Size

Total Lines 35
Code Lines 22

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 8.439
c 0
b 0
f 0
cc 6
eloc 22
nc 7
nop 5
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * balloon
7
 *
8
 * @copyright   Copryright (c) 2012-2018 gyselroth GmbH (https://gyselroth.com)
9
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
10
 */
11
12
namespace Balloon\App\Notification\Api\v2;
13
14
use Balloon\App\Api\Controller;
15
use Balloon\App\Notification\AttributeDecorator as NotificationAttributeDecorator;
16
use Balloon\App\Notification\Notifier;
17
use Balloon\Async\Mail;
18
use Balloon\AttributeDecorator\Pager;
19
use Balloon\Filesystem;
20
use Balloon\Filesystem\Acl\Exception\Forbidden as ForbiddenException;
21
use Balloon\Filesystem\Node\AttributeDecorator as NodeAttributeDecorator;
22
use Balloon\Server;
23
use Balloon\Server\AttributeDecorator as RoleAttributeDecorator;
24
use Balloon\Server\User;
25
use Micro\Http\Response;
26
use MongoDB\BSON\ObjectId;
27
use Psr\Log\LoggerInterface;
28
use TaskScheduler\Async;
29
use Zend\Mail\Message;
30
31
class Notifications extends Controller
32
{
33
    /**
34
     * Notifier.
35
     *
36
     * @var Notifier
37
     */
38
    protected $notifier;
39
40
    /**
41
     * User.
42
     *
43
     * @var User
44
     */
45
    protected $user;
46
47
    /**
48
     * Filesystem.
49
     *
50
     * @var Filesystem
51
     */
52
    protected $fs;
53
54
    /**
55
     * Server.
56
     *
57
     * @var Server
58
     */
59
    protected $server;
60
61
    /**
62
     * Async.
63
     *
64
     * @var Async
65
     */
66
    protected $async;
67
68
    /**
69
     * Logger.
70
     *
71
     * @var LoggerInterface
72
     */
73
    protected $logger;
74
75
    /**
76
     * Role attribute decorator.
77
     *
78
     * @var RoleAttributeDecorator
79
     */
80
    protected $role_decorator;
81
82
    /**
83
     * Role attribute decorator.
84
     *
85
     * @var NodeAttributeDecorator
86
     */
87
    protected $node_decorator;
88
89
    /**
90
     * Notification attribute decorator.
91
     *
92
     * @var NotificationAttributeDecorator
93
     */
94
    protected $notification_decorator;
95
96
    /**
97
     * Constructor.
98
     *
99
     * @param Notifier                       $notifier
100
     * @param Server                         $server
101
     * @param Async                          $async
102
     * @param LoggerInterface                $logger
103
     * @param RoleAttributeDecorator         $role_decorator
104
     * @param NodeAttributeDecorator         $node_decorator
105
     * @param NotificationAttributeDecorator $notification_decorator
106
     */
107
    public function __construct(Notifier $notifier, Server $server, Async $async, LoggerInterface $logger, RoleAttributeDecorator $role_decorator, NodeAttributeDecorator $node_decorator, NotificationAttributeDecorator $notification_decorator)
108
    {
109
        $this->notifier = $notifier;
110
        $this->user = $server->getIdentity();
111
        $this->fs = $server->getFilesystem();
112
        $this->server = $server;
113
        $this->async = $async;
114
        $this->logger = $logger;
115
        $this->role_decorator = $role_decorator;
116
        $this->node_decorator = $node_decorator;
117
        $this->notification_decorator = $notification_decorator;
118
    }
119
120
    /**
121
     * @api {get} /api/v2/notifications Get notifications
122
     * @apiVersion 2.0.0
123
     * @apiName get
124
     * @apiGroup App\Notification
125
     * @apiPermission none
126
     * @apiDescription Fetch my nofitifications
127
     *
128
     * @apiExample (cURL) exmaple:
129
     * curl -XGET "https://SERVER/api/v2/notification"
130
     *
131
     * @apiSuccessExample {string} Success-Response:
132
     * HTTP/1.1 200 OK
133
     * [
134
     *  "id": "",
135
     *  "message": "Hi there, this is a notification",
136
     *  "subject": "Hi",
137
     *  "sender": {
138
     *      "id": "",
139
     *      "name": ""
140
     *  }
141
     * ]
142
     *
143
     * @param ObjectId $id
144
     * @param array    $attributes
145
     * @param int      $offset
146
     * @param int      $limit
147
     */
148
    public function get(?ObjectId $id = null, array $attributes = [], int $offset = 0, int $limit = 20): Response
149
    {
150
        if ($id !== null) {
151
            $message = $this->notifier->getNotification($id);
152
            $result = $this->notification_decorator->decorate($message, $attributes);
153
154
            return (new Response())->setCode(200)->setBody($result);
155
        }
156
157
        $result = $this->notifier->getNotifications($this->user, $offset, $limit, $total);
158
        $uri = '/api/v2/notifications';
159
        $pager = new Pager($this->notification_decorator, $result, $attributes, $offset, $limit, $uri, $total);
160
        $result = $pager->paging();
161
162
        return (new Response())->setCode(200)->setBody($result);
163
    }
164
165
    /**
166
     * @api {delete} /api/v2/notifications Delete notification
167
     * @apiVersion 2.0.0
168
     * @apiName delete
169
     * @apiGroup App\Notification
170
     * @apiPermission none
171
     * @apiDescription Fetch my nofitifications
172
     *
173
     * @apiExample (cURL) exmaple:
174
     * curl -XGET "https://SERVER/api/v2/notification"
175
     *
176
     * @apiSuccessExample {string} Success-Response:
177
     * HTTP/1.1 204 No Content
178
     */
179
    public function delete(ObjectId $id): Response
180
    {
181
        $this->notifier->deleteNotification($id);
182
183
        return (new Response())->setCode(204);
184
    }
185
186
    /**
187
     * @api {post} /api/v2/notifications Post a notification to a group of users
188
     * @apiVersion 2.0.0
189
     * @apiName post
190
     * @apiGroup App\Notification
191
     * @apiPermission none
192
     * @apiDescription Send notification
193
     *
194
     * @apiExample (cURL) exmaple:
195
     * curl -XPOST "https://SERVER/api/v2/notification"
196
     *
197
     * @apiSuccessExample {string} Success-Response:
198
     * HTTP/1.1 202 Accepted
199
     */
200
    public function post(array $receiver, string $subject, string $body): Response
201
    {
202
        $users = $this->server->getUsersById($receiver);
0 ignored issues
show
Unused Code introduced by
$users is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
203
        $this->notifier->notify($receiver, $this->user, $subject, $body);
0 ignored issues
show
Documentation introduced by
$receiver is of type array, but the function expects a object<Balloon\App\Notification\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$subject is of type string, but the function expects a object<Balloon\App\Notification\MessageInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$body is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
204
205
        return (new Response())->setCode(202);
206
    }
207
208
    /**
209
     * @api {post} /api/v2/notifications/broadcast Post a notification to all users (or to a bunch of users)
210
     * @apiVersion 2.0.0
211
     * @apiName postBroadcast
212
     * @apiGroup App\Notification
213
     * @apiPermission admin
214
     * @apiDescription Send notification
215
     *
216
     * @apiExample (cURL) exmaple:
217
     * curl -XPOST "https://SERVER/api/v2/notifications/broadcast"
218
     *
219
     * @apiSuccessExample {string} Success-Response:
220
     * HTTP/1.1 202 Accepted
221
     */
222
    public function postBroadcast(string $subject, string $body): Response
223
    {
224
        if (!$this->user->isAdmin()) {
225
            throw new ForbiddenException(
226
                'submitted parameters require to have admin privileges',
227
                    ForbiddenException::ADMIN_PRIV_REQUIRED
228
                );
229
        }
230
231
        $users = $this->server->getUsers();
0 ignored issues
show
Bug introduced by
The call to getUsers() misses a required argument $filter.

This check looks for function calls that miss required arguments.

Loading history...
232
        $this->notifier->notify($users, $this->user, $subject, $body);
0 ignored issues
show
Documentation introduced by
$users is of type object<Generator>, but the function expects a object<Balloon\App\Notification\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$subject is of type string, but the function expects a object<Balloon\App\Notification\MessageInterface>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
Documentation introduced by
$body is of type string, but the function expects a array.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
233
234
        return (new Response())->setCode(202);
235
    }
236
237
    /**
238
     * @api {post} /api/v2/notifications/mail Send a mail
239
     * @apiVersion 2.0.0
240
     * @apiName postMail
241
     * @apiGroup App\Notification
242
     * @apiPermission none
243
     * @apiDescription Send mail
244
     *
245
     * @apiExample (cURL) exmaple:
246
     * curl -XGET "https://SERVER/api/v2/notifications/mail"
247
     *
248
     * @apiSuccessExample {string} Success-Response:
249
     * HTTP/1.1 202 Accepted
250
     */
251
    public function postMail(array $receiver, string $subject, string $body)
252
    {
253
        $mail = new Message();
254
        $mail->setBody($body)
255
          ->setFrom($this->user->getAttributes()['mail'], $this->user->getAttributes()['username'])
256
          ->setSubject($subject)
257
          ->setTo($this->user->getAttributes()['mail'], 'Undisclosed Recipients')
258
          ->setBcc($receiver);
259
        $this->async->addJob(Mail::class, $mail->toString());
260
261
        return (new Response())->setCode(202);
262
    }
263
264
    /**
265
     * @api {post} /api/v2/notifications/subscribe Subscribe for node update
266
     * @apiVersion 2.0.0
267
     * @apiName postSubscribe
268
     * @apiGroup App\Notification
269
     * @apiPermission none
270
     * @apiDescription Receive node updates
271
     * @apiUse _getNodes
272
     * @apiUse _multiError
273
     *
274
     * @apiExample (cURL) exmaple:
275
     * curl -XPOST "https://SERVER/api/v2/notifications/subscribe"
276
     *
277
     * @apiSuccessExample {string} Success-Response:
278
     * HTTP/1.1 200 OK
279
     * {
280
     *      "id": ""
281
     * }
282
     *
283
     * @param null|mixed $id
284
     * @param null|mixed $p
285
     */
286
    public function postSubscribtions($id = null, $p = null, bool $subscribe = true, bool $exclude_me = true, bool $recursive = false)
287
    {
288
        if (is_array($id) || is_array($p)) {
289
            foreach ($this->fs->findNodesById($id) as $node) {
290
                try {
291
                    $this->notifier->subscribeNode($this->fs->getNode($id, $p), $subscribe);
292
                } catch (\Exception $e) {
293
                    $failures[] = [
0 ignored issues
show
Coding Style Comprehensibility introduced by
$failures was never initialized. Although not strictly required by PHP, it is generally a good practice to add $failures = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
294
                        'id' => (string) $node->getId(),
295
                        'name' => $node->getName(),
296
                        'error' => get_class($e),
297
                        'message' => $e->getMessage(),
298
                        'code' => $e->getCode(),
299
                    ];
300
301
                    $this->logger->debug('failed subscribe node in multi node request ['.$node->getId().']', [
302
                        'category' => get_class($this),
303
                        'exception' => $e,
304
                    ]);
305
                }
306
            }
307
308
            if (empty($failures)) {
309
                return (new Response())->setCode(204);
310
            }
311
312
            return (new Response())->setcode(400)->setBody($failures);
313
        }
314
315
        $node = $this->fs->getNode($id, $p);
316
        $this->notifier->subscribeNode($node, $subscribe, $exclude_me, $recursive);
317
        $result = $this->node_decorator->decorate($node);
318
319
        return (new Response())->setCode(200)->setBody($result);
320
    }
321
}
322