Completed
Push — master ( 8c9911...54b06b )
by Raffael
26:03 queued 22:08
created

Nodes::getContent()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
dl 0
loc 19
ccs 0
cts 16
cp 0
rs 9.6333
c 0
b 0
f 0
cc 4
nc 3
nop 4
crap 20
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\Api\v2;
13
14
use Balloon\App\Api\Controller;
15
use Balloon\App\Api\Helper as ApiHelper;
16
use Balloon\App\Api\v2\Collections as ApiCollection;
17
use Balloon\App\Api\v2\Files as ApiFile;
18
use Balloon\AttributeDecorator\Pager;
19
use Balloon\Filesystem;
20
use Balloon\Filesystem\DeltaAttributeDecorator;
21
use Balloon\Filesystem\EventAttributeDecorator;
22
use Balloon\Filesystem\Exception;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Balloon\App\Api\v2\Exception.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
23
use Balloon\Filesystem\Node\AttributeDecorator as NodeAttributeDecorator;
24
use Balloon\Filesystem\Node\Collection;
25
use Balloon\Filesystem\Node\File;
26
use Balloon\Filesystem\Node\NodeInterface;
27
use Balloon\Helper;
28
use Balloon\Server;
29
use Balloon\Server\User;
30
use Micro\Http\Response;
31
use MongoDB\BSON\UTCDateTime;
32
use Psr\Log\LoggerInterface;
33
use ZipStream\ZipStream;
34
35
class Nodes extends Controller
36
{
37
    /**
38
     * Filesystem.
39
     *
40
     * @var Filesystem
41
     */
42
    protected $fs;
43
44
    /**
45
     * LoggerInterface.
46
     *
47
     * @var LoggerInterface
48
     */
49
    protected $logger;
50
51
    /**
52
     * Server.
53
     *
54
     * @var Server
55
     */
56
    protected $server;
57
58
    /**
59
     * User.
60
     *
61
     * @var User
62
     */
63
    protected $user;
64
65
    /**
66
     * Node attribute decorator.
67
     *
68
     * @var NodeAttributeDecorator
69
     */
70
    protected $node_decorator;
71
72
    /**
73
     * Initialize.
74
     */
75
    public function __construct(Server $server, NodeAttributeDecorator $decorator, LoggerInterface $logger)
76
    {
77
        $this->fs = $server->getFilesystem();
78
        $this->user = $server->getIdentity();
79
        $this->server = $server;
80
        $this->node_decorator = $decorator;
81
        $this->logger = $logger;
82
    }
83
84
    /**
85
     * @api {head} /api/v2/nodes/:id Node exists?
86
     * @apiVersion 2.0.0
87
     * @apiName head
88
     * @apiGroup Node
89
     * @apiPermission none
90
     * @apiDescription Check if a node exists. Per default deleted nodes are ignore which means it will
91
     *  return a 404 if a deleted node is requested. You can change this behaviour via the deleted parameter.
92
     * @apiUse _getNode
93
     *
94
     * @apiExample (cURL) example:
95
     * curl -XHEAD "https://SERVER/api/v2/node?id=544627ed3c58891f058b4686"
96
     * curl -XHEAD "https://SERVER/api/v2/nodes/544627ed3c58891f058b4686"
97
     * curl -XHEAD "https://SERVER/api/v2/node?p=/absolute/path/to/my/node"
98
     *
99
     * @apiParam (GET Parameter) {number} [deleted=0] Wherever include deleted node or not, possible values:</br>
100
     * - 0 Exclude deleted</br>
101
     * - 1 Only deleted</br>
102
     * - 2 Include deleted</br>
103
     *
104
     * @apiSuccessExample {json} Success-Response (Node does exist):
105
     * HTTP/1.1 200 OK
106
     *
107
     * @apiSuccessExample {json} Success-Response (Node does not exist):
108
     * HTTP/1.1 404 Not Found
109
     *
110
     * @param string $id
111
     * @param string $p
112
     */
113
    public function head(?string $id = null, ?string $p = null, int $deleted = 0): Response
114
    {
115
        try {
116
            $result = $this->_getNode($id, $p, null, false, false, $deleted);
117
118
            $response = (new Response())
119
                ->setHeader('Content-Length', (string) $result->getSize())
120
                ->setHeader('Content-Type', $result->getContentType())
121
                ->setCode(200);
122
123
            return $response;
124
        } catch (\Exception $e) {
125
            return (new Response())->setCode(404);
126
        }
127
    }
128
129
    /**
130
     * @api {post} /api/v2/nodes/:id/undelete Restore node
131
     * @apiVersion 2.0.0
132
     * @apiName postUndelete
133
     * @apiGroup Node
134
     * @apiPermission none
135
     * @apiDescription Undelete (Restore from trash) a single node or multiple ones.
136
     * @apiUse _getNodes
137
     * @apiUse _conflictNode
138
     * @apiUse _multiError
139
     * @apiUse _writeAction
140
     *
141
     * @apiExample (cURL) example:
142
     * curl -XPOST "https://SERVER/api/v2/nodes/undelete?id[]=544627ed3c58891f058b4686&id[]=544627ed3c58891f058b46865&pretty"
143
     * curl -XPOST "https://SERVER/api/v2/nodes/undelete?id=544627ed3c58891f058b4686?pretty"
144
     * curl -XPOST "https://SERVER/api/v2/nodes/544627ed3c58891f058b4686/undelete?conflict=2"
145
     * curl -XPOST "https://SERVER/api/v2/nodes/undelete?p=/absolute/path/to/my/node&conflict=0&move=1&destid=544627ed3c58891f058b46889"
146
     *
147
     * @apiParam (GET Parameter) {string} [destid] Either destid or destp (path) of the new parent collection node must be given.
148
     * @apiParam (GET Parameter) {string} [destp] Either destid or destp (path) of the new parent collection node must be given.
149
     *
150
     * @apiSuccessExample {json} Success-Response (conflict=1):
151
     * HTTP/1.1 200 OK
152
     * {
153
     *      "id": "544627ed3c58891f058b4686",
154
     *      "name": "renamed (xy23)"
155
     * }
156
     *
157
     * @param array|string $id
158
     * @param array|string $p
159
     * @param string       $destid
160
     * @param string       $destp
161
     */
162
    public function postUndelete(
163
        $id = null,
164
        $p = null,
165
        bool $move = false,
166
        ?string $destid = null,
167
        ?string $destp = null,
168
        int $conflict = 0
169
    ): Response {
170
        $parent = null;
171
        if (true === $move) {
172
            try {
173
                $parent = $this->_getNode($destid, $destp, 'Collection', false, true);
174
            } catch (Exception\NotFound $e) {
175
                throw new Exception\NotFound(
176
                    'destination collection was not found or is not a collection',
177
                    Exception\NotFound::DESTINATION_NOT_FOUND
178
                );
179
            }
180
        }
181
182
        return $this->bulk($id, $p, function ($node) use ($parent, $conflict, $move) {
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 163 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $p defined by parameter $p on line 164 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
183
            if (true === $move) {
184
                $node = $node->setParent($parent, $conflict);
185
            }
186
187
            if ($node->isDeleted()) {
188
                $node->undelete($conflict);
189
            }
190
191
            return [
192
                'code' => 200,
193
                'data' => $this->node_decorator->decorate($node),
194
            ];
195
        });
196
    }
197
198
    /**
199
     * @api {get} /api/v2/nodes/:id/content Download stream
200
     * @apiVersion 2.0.0
201
     * @apiName getContent
202
     * @apiGroup Node
203
     * @apiPermission none
204
     * @apiDescription Download node contents. Collections are zipped during streaming.
205
     * @apiUse _getNode
206
     * @apiParam (GET Parameter) {boolean} [download=false] Force download file (Content-Disposition: attachment HTTP header)
207
     *
208
     * @apiExample (cURL) example:
209
     * curl -XGET "https://SERVER/api/v2/node?id=544627ed3c58891f058b4686" > myfile.txt
210
     * curl -XGET "https://SERVER/api/v2/nodes/544627ed3c58891f058b4686" > myfile.txt
211
     * curl -XGET "https://SERVER/api/v2/node?p=/absolute/path/to/my/collection" > folder.zip
212
     *
213
     * @apiSuccessExample {binary} Success-Response:
214
     * HTTP/1.1 200 OK
215
     *
216
     * @apiErrorExample {json} Error-Response (Invalid offset):
217
     * HTTP/1.1 400 Bad Request
218
     * {
219
     *      "status": 400,
220
     *      "data": {
221
     *          "error": "Balloon\\Exception\\Conflict",
222
     *          "message": "invalid offset requested",
223
     *          "code": 277
224
     *      }
225
     * }
226
     *
227
     * @param array|string $id
228
     * @param array|string $p
229
     */
230
    public function getContent(
231
        $id = null,
232
        $p = null,
233
        bool $download = false,
234
        string $name = 'selected'
235
    ): ?Response {
236
        if (is_array($id) || is_array($p)) {
237
            return $this->combine($id, $p, $name);
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 231 can also be of type array; however, Balloon\App\Api\v2\Nodes::combine() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $p defined by parameter $p on line 232 can also be of type array; however, Balloon\App\Api\v2\Nodes::combine() does only seem to accept string|null, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
238
        }
239
240
        $node = $this->_getNode($id, $p);
241
        if ($node instanceof Collection) {
242
            return $node->getZip();
243
        }
244
245
        $response = new Response();
246
247
        return ApiHelper::streamContent($response, $node, $download);
248
    }
249
250
    /**
251
     * @apiDefine _nodeAttributes
252
     *
253
     * @apiSuccess (200 OK) {string} id Unique node id
254
     * @apiSuccess (200 OK) {string} name Name
255
     * @apiSuccess (200 OK) {string} hash MD5 content checksum (file only)
256
     * @apiSuccess (200 OK) {object} meta Extended meta attributes
257
     * @apiSuccess (200 OK) {string} meta.description UTF-8 Text Description
258
     * @apiSuccess (200 OK) {string} meta.color Color Tag (HEX) (Like: #000000)
259
     * @apiSuccess (200 OK) {string} meta.author Author
260
     * @apiSuccess (200 OK) {string} meta.mail Mail contact address
261
     * @apiSuccess (200 OK) {string} meta.license License
262
     * @apiSuccess (200 OK) {string} meta.copyright Copyright string
263
     * @apiSuccess (200 OK) {string[]} meta.tags Search Tags
264
     * @apiSuccess (200 OK) {number} size Size in bytes (file only), number of children if collection
265
     * @apiSuccess (200 OK) {string} mime Mime type
266
     * @apiSuccess (200 OK) {boolean} sharelink Is node shared?
267
     * @apiSuccess (200 OK) {number} version File version (file only)
268
     * @apiSuccess (200 OK) {mixed} deleted Is boolean false if not deleted, if deleted it contains a deleted timestamp
269
     * @apiSuccess (200 OK) {string} deleted ISO8601 timestamp, only set if node is deleted
270
     * @apiSuccess (200 OK) {string} changed ISO8601 timestamp
271
     * @apiSuccess (200 OK) {string} created ISO8601 timestamp
272
     * @apiSuccess (200 OK) {string} destroy ISO8601 timestamp, only set if node has a destroy timestamp set
273
     * @apiSuccess (200 OK) {boolean} share Node is shared
274
     * @apiSuccess (200 OK) {boolean} directory Is true if the node is a collection
275
     * @apiSuccess (200 OK) {string} access Access permission for the authenticated user (d/r/rw/m)
276
     * @apiSuccess (200 OK) {object} shareowner Share owner
277
     * @apiSuccess (200 OK) {object} parent Parent node
278
     * @apiSuccess (200 OK) {string} path Absolute node path
279
     * @apiSuccess (200 OK) {string} filter Node is filtered (collection only)
280
     * @apiSuccess (200 OK) {boolean} readonly Readonly
281
     *
282
     * @apiParam (GET Parameter) {string[]} [attributes] Filter attributes
283
     *
284
     * @param null|mixed $id
285
     * @param null|mixed $p
286
     */
287
288
    /**
289
     * @api {get} /api/v2/nodes/:id Get attributes
290
     * @apiVersion 2.0.0
291
     * @apiName get
292
     * @apiGroup Node
293
     * @apiPermission none
294
     * @apiDescription Get attributes from one or multiple nodes
295
     * @apiUse _getNode
296
     * @apiUse _nodeAttributes
297
     *
298
     * @apiParam (GET Parameter) {string[]} [attributes] Filter attributes, per default only a bunch of attributes would be returned, if you
299
     * need other attributes you have to request them (for example "path")
300
     *
301
     * @apiExample (cURL) example:
302
     * curl -XGET "https://SERVER/api/v2/node?id=544627ed3c58891f058b4686&pretty"
303
     * curl -XGET "https://SERVER/api/v2/node?id=544627ed3c58891f058b4686&attributes[0]=name&attributes[1]=deleted&pretty"
304
     * curl -XGET "https://SERVER/api/v2/nodes/544627ed3c58891f058b4686?pretty"
305
     * curl -XGET "https://SERVER/api/v2/node?p=/absolute/path/to/my/node&pretty"
306
     *
307
     * @apiSuccessExample {json} Success-Response:
308
     * HTTP/1.1 200 OK
309
     * {
310
     *      "id": "544627ed3c58891f058b4686",
311
     *      "name": "api.php",
312
     *      "hash": "a77f23ed800fd7a600a8c2cfe8cc370b",
313
     *      "meta": {
314
     *          "license": "GPLv3"
315
     *      },
316
     *      "size": 178,
317
     *      "mime": "text\/plain",
318
     *      "sharelink": true,
319
     *      "version": 1,
320
     *      "changed": "2007-08-31T16:47+00:00",
321
     *      "created": "2007-08-31T16:47+00:00",
322
     *      "share": false,
323
     *      "directory": false
324
     * }
325
     *
326
     * @param array|string $id
327
     * @param array|string $p
328
     */
329
    public function get($id = null, $p = null, int $deleted = 0, array $query = [], array $attributes = [], int $offset = 0, int $limit = 20): Response
330
    {
331
        if ($id === null && $p === null) {
332
            if ($this instanceof ApiFile) {
333
                $query['directory'] = false;
334
                $uri = '/api/v2/files';
335
            } elseif ($this instanceof ApiCollection) {
336
                $query['directory'] = true;
337
                $uri = '/api/v2/collections';
338
            } else {
339
                $uri = '/api/v2/nodes';
340
            }
341
342
            $nodes = $this->fs->findNodesByFilterUser($deleted, $query, $offset, $limit);
343
            $pager = new Pager($this->node_decorator, $nodes, $attributes, $offset, $limit, $uri);
344
            $result = $pager->paging();
345
346
            return (new Response())->setCode(200)->setBody($result);
347
        }
348
349
        return $this->bulk($id, $p, function ($node) use ($attributes) {
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 329 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $p defined by parameter $p on line 329 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
350
            return [
351
                'code' => 200,
352
                'data' => $this->node_decorator->decorate($node, $attributes),
353
            ];
354
        });
355
    }
356
357
    /**
358
     * @api {get} /api/v2/nodes/:id/parents Get parent nodes
359
     * @apiVersion 2.0.0
360
     * @apiName getParents
361
     * @apiGroup Node
362
     * @apiPermission none
363
     * @apiDescription Get system attributes of all parent nodes. The hirarchy of all parent nodes is ordered in a
364
     * single level array beginning with the collection on the highest level.
365
     * @apiUse _getNode
366
     * @apiUse _nodeAttributes
367
     *
368
     * @apiParam (GET Parameter) {boolean} [self=true] Include requested collection itself at the end of the list (Will be ignored if the requested node is a file)
369
     *
370
     * @apiExample (cURL) example:
371
     * curl -XGET "https://SERVER/api/v2/nodes/parents?id=544627ed3c58891f058b4686&pretty"
372
     * curl -XGET "https://SERVER/api/v2/nodes/parents?id=544627ed3c58891f058b4686&attributes[0]=name&attributes[1]=deleted&pretty"
373
     * curl -XGET "https://SERVER/api/v2/nodes/544627ed3c58891f058b4686/parents?pretty&self=1"
374
     * curl -XGET "https://SERVER/api/v2/nodes/parents?p=/absolute/path/to/my/node&self=1"
375
     *
376
     * @apiSuccessExample {json} Success-Response:
377
     * HTTP/1.1 200 OK
378
     * [
379
     *  {
380
     *      "id": "544627ed3c58891f058bbbaa",
381
     *      "name": "rootdir",
382
     *      "meta": {},
383
     *      "size": 1,
384
     *      "mime": "inode/directory",
385
     *      "created": "2007-08-31T16:47+00:00",
386
     *      "changed": "2007-08-31T16:47+00:00",
387
     *      "destroy": "2020-08-31T16:47+00:00",
388
     *      "share": false,
389
     *      "directory": true
390
     *  },
391
     *  {
392
     *      "id": "544627ed3c58891f058b46cc",
393
     *      "name": "parentdir",
394
     *      "meta": {},
395
     *      "size": 3,
396
     *      "mime": "inode/directory",
397
     *      "created": "2007-08-31T16:47+00:00",
398
     *      "changed": "2007-08-31T16:47+00:00",
399
     *      "share": false,
400
     *      "directory": true
401
     *  }
402
     * ]
403
     *
404
     * @param string $id
405
     * @param string $p
406
     */
407
    public function getParents(?string $id = null, ?string $p = null, array $attributes = [], bool $self = false): Response
408
    {
409
        $result = [];
410
        $request = $this->_getNode($id, $p);
411
        $parents = $request->getParents();
412
413
        if (true === $self && $request instanceof Collection) {
414
            $result[] = $this->node_decorator->decorate($request, $attributes);
415
        }
416
417
        foreach ($parents as $node) {
418
            $result[] = $this->node_decorator->decorate($node, $attributes);
419
        }
420
421
        return (new Response())->setCode(200)->setBody($result);
422
    }
423
424
    /**
425
     * @api {patch} /api/v2/nodes/:id Change attributes
426
     * @apiVersion 2.0.0
427
     * @apiName patch
428
     * @apiGroup Node
429
     * @apiPermission none
430
     * @apiDescription Change attributes
431
     * @apiUse _getNodes
432
     * @apiUse _multiError
433
     *
434
     * @apiParam (GET Parameter) {string} [name] Rename node, the characters (\ < > : " / * ? |) (without the "()") are not allowed to use within a node name.
435
     * @apiParam (GET Parameter) {boolean} [readonly] Mark node as readonly
436
     * @apiParam (GET Parameter) {object} [filter] Custom collection filter (Collection only)
437
     * @apiParam (GET Parameter) {string} [meta.description] UTF-8 Text Description - Can contain anything as long as it is a string
438
     * @apiParam (GET Parameter) {string} [meta.color] Color Tag - Can contain anything as long as it is a string
439
     * @apiParam (GET Parameter) {string} [meta.author] Author - Can contain anything as long as it is a string
440
     * @apiParam (GET Parameter) {string} [meta.mail] Mail contact address - Can contain anything as long as it is a string
441
     * @apiParam (GET Parameter) {string} [meta.license] License - Can contain anything as long as it is a string
442
     * @apiParam (GET Parameter) {string} [meta.copyright] Copyright string - Can contain anything as long as it is a string
443
     * @apiParam (GET Parameter) {string[]} [meta.tags] Tags - Must be an array full of strings
444
     *
445
     * @apiExample (cURL) example:
446
     * curl -XPATCH "https://SERVER/api/v2/nodes/544627ed3c58891f058b4686?name=example"
447
     *
448
     * @apiSuccessExample {json} Success-Response:
449
     * HTTP/1.1 200
450
     *
451
     * @param array|string $id
452
     * @param array|string $p
453
     */
454
    public function patch(?string $name = null, ?array $meta = null, ?bool $readonly = null, ?array $filter = null, ?array $acl = null, ?string $id = null, ?string $p = null): Response
455
    {
456
        $attributes = compact('name', 'meta', 'readonly', 'filter', 'acl');
457
        $attributes = array_filter($attributes, function ($attribute) {return !is_null($attribute); });
458
459
        return $this->bulk($id, $p, function ($node) use ($attributes) {
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 454 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $p defined by parameter $p on line 454 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
460
            foreach ($attributes as $attribute => $value) {
461
                switch ($attribute) {
462
                    case 'name':
463
                        $node->setName($value);
464
465
                    break;
466
                    case 'meta':
467
                        $node->setMetaAttributes($value);
468
469
                    break;
470
                    case 'readonly':
471
                        $node->setReadonly($value);
472
473
                    break;
474
                    case 'filter':
475
                        if ($node instanceof Collection) {
476
                            $node->setFilter($value);
477
                        }
478
479
                    break;
480
                    case 'acl':
481
                        $node->setAcl($value);
482
483
                    break;
484
                }
485
            }
486
487
            return [
488
                'code' => 200,
489
                'data' => $this->node_decorator->decorate($node),
490
            ];
491
        });
492
    }
493
494
    /**
495
     * @api {post} /api/v2/nodes/:id/clone Clone node
496
     * @apiVersion 2.0.0
497
     * @apiName postClone
498
     * @apiGroup Node
499
     * @apiPermission none
500
     * @apiDescription Clone a node
501
     * @apiUse _getNode
502
     * @apiUse _conflictNode
503
     * @apiUse _multiError
504
     * @apiUse _writeAction
505
     *
506
     * @apiParam (GET Parameter) {string} [destid] Either destid or destp (path) of the new parent collection node must be given.
507
     * @apiParam (GET Parameter) {string} [destp] Either destid or destp (path) of the new parent collection node must be given.
508
     *
509
     * @apiExample (cURL) example:
510
     * curl -XPOST "https://SERVER/api/v2/nodes/clone?id=544627ed3c58891f058b4686&dest=544627ed3c58891f058b4676"
511
     * curl -XPOST "https://SERVER/api/v2/nodes/544627ed3c58891f058b4686/clone?dest=544627ed3c58891f058b4676&conflict=2"
512
     * curl -XPOST "https://SERVER/api/v2/nodes/clone?p=/absolute/path/to/my/node&conflict=0&destp=/new/parent"
513
     *
514
     * @apiSuccessExample {json} Success-Response:
515
     * HTTP/1.1 201 Created
516
     *
517
     * @apiSuccessExample {json} Success-Response:
518
     * HTTP/1.1 200 OK
519
     *
520
     * @param array|string $id
521
     * @param array|string $id
522
     * @param array|string $p
523
     * @param string       $destid
524
     * @param string       $destp
525
     */
526
    public function postClone(
527
        $id = null,
528
        $p = null,
529
        ?string $destid = null,
530
        ?string $destp = null,
531
        int $conflict = 0
532
    ): Response {
533
        try {
534
            $parent = $this->_getNode($destid, $destp, Collection::class, false, true);
535
        } catch (Exception\NotFound $e) {
536
            throw new Exception\NotFound(
537
                'destination collection was not found or is not a collection',
538
                Exception\NotFound::DESTINATION_NOT_FOUND
539
            );
540
        }
541
542
        return $this->bulk($id, $p, function ($node) use ($parent, $conflict) {
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 527 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $p defined by parameter $p on line 528 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
543
            $result = $node->copyTo($parent, $conflict);
544
545
            return [
546
                'code' => $parent == $result ? 200 : 201,
547
                'data' => $this->node_decorator->decorate($result),
548
            ];
549
        });
550
    }
551
552
    /**
553
     * @api {post} /api/v2/nodes/:id/move Move node
554
     * @apiVersion 2.0.0
555
     * @apiName postMove
556
     * @apiGroup Node
557
     * @apiPermission none
558
     * @apiDescription Move node
559
     * @apiUse _getNodes
560
     * @apiUse _conflictNode
561
     * @apiUse _multiError
562
     * @apiUse _writeAction
563
     *
564
     * @apiParam (GET Parameter) {string} [destid] Either destid or destp (path) of the new parent collection node must be given.
565
     * @apiParam (GET Parameter) {string} [destp] Either destid or destp (path) of the new parent collection node must be given.
566
     *
567
     * @apiExample (cURL) example:
568
     * curl -XPOST "https://SERVER/api/v2/nodes/move?id=544627ed3c58891f058b4686?destid=544627ed3c58891f058b4655"
569
     * curl -XPOST "https://SERVER/api/v2/nodes/544627ed3c58891f058b4686/move?destid=544627ed3c58891f058b4655"
570
     * curl -XPOST "https://SERVER/api/v2/nodes/move?p=/absolute/path/to/my/node&destp=/new/parent&conflict=1
571
     *
572
     * @apiSuccessExample {json} Success-Response:
573
     * HTTP/1.1 204 No Content
574
     *
575
     * @apiSuccessExample {json} Success-Response (conflict=1):
576
     * HTTP/1.1 200 OK
577
     * {
578
     *      "status":200,
579
     *      "data": "renamed (xy23)"
580
     * }
581
     *
582
     * @param array|string $id
583
     * @param array|string $p
584
     * @param string       $destid
585
     * @param string       $destp
586
     */
587
    public function postMove(
588
        $id = null,
589
        $p = null,
590
        ?string $destid = null,
591
        ?string $destp = null,
592
        int $conflict = 0
593
    ): Response {
594
        try {
595
            $parent = $this->_getNode($destid, $destp, Collection::class, false, true);
596
        } catch (Exception\NotFound $e) {
597
            throw new Exception\NotFound(
598
                'destination collection was not found or is not a collection',
599
                Exception\NotFound::DESTINATION_NOT_FOUND
600
            );
601
        }
602
603
        return $this->bulk($id, $p, function ($node) use ($parent, $conflict) {
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 588 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $p defined by parameter $p on line 589 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
604
            $result = $node->setParent($parent, $conflict);
0 ignored issues
show
Unused Code introduced by
$result 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...
605
606
            return [
607
                'code' => 200,
608
                'data' => $this->node_decorator->decorate($node),
609
            ];
610
        });
611
    }
612
613
    /**
614
     * @api {delete} /api/v2/nodes/:id Delete node
615
     * @apiVersion 2.0.0
616
     * @apiName delete
617
     * @apiGroup Node
618
     * @apiPermission none
619
     * @apiDescription Delete node
620
     * @apiUse _getNodes
621
     * @apiUse _multiError
622
     * @apiUse _writeAction
623
     *
624
     * @apiParam (GET Parameter) {boolean} [force=false] Force flag need to be set to delete a node from trash (node must have the deleted flag)
625
     * @apiParam (GET Parameter) {boolean} [ignore_flag=false] If both ignore_flag and force_flag were set, the node will be deleted completely
626
     * @apiParam (GET Parameter) {number} [at] Has to be a valid unix timestamp if so the node will destroy itself at this specified time instead immediatly
627
     *
628
     * @apiExample (cURL) example:
629
     * curl -XDELETE "https://SERVER/api/v2/node?id=544627ed3c58891f058b4686"
630
     * curl -XDELETE "https://SERVER/api/v2/nodes/544627ed3c58891f058b4686?force=1&ignore_flag=1"
631
     * curl -XDELETE "https://SERVER/api/v2/node?p=/absolute/path/to/my/node"
632
     *
633
     * @apiSuccessExample {json} Success-Response:
634
     * HTTP/1.1 204 No Content
635
     *
636
     * @param array|string $id
637
     * @param array|string $p
638
     * @param int          $at
639
     */
640
    public function delete(
641
        $id = null,
642
        $p = null,
643
        bool $force = false,
644
        bool $ignore_flag = false,
645
        ?string $at = null
646
    ): Response {
647
        $failures = [];
0 ignored issues
show
Unused Code introduced by
$failures 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...
648
649
        if (null !== $at && '0' !== $at) {
0 ignored issues
show
Unused Code Bug introduced by
The strict comparison !== seems to always evaluate to true as the types of '0' (string) and $at (integer) can never be identical. Maybe you want to use a loose comparison != instead?
Loading history...
650
            $at = $this->_verifyAttributes(['destroy' => $at])['destroy'];
651
        }
652
653
        return $this->bulk($id, $p, function ($node) use ($force, $ignore_flag, $at) {
0 ignored issues
show
Bug introduced by
It seems like $id defined by parameter $id on line 641 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
Bug introduced by
It seems like $p defined by parameter $p on line 642 can also be of type null; however, Balloon\App\Api\Controller::bulk() does only seem to accept array|string, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
654
            if (null === $at) {
655
                $node->delete($force && $node->isDeleted() || $force && $ignore_flag);
656
            } else {
657
                if ('0' === $at) {
658
                    $at = null;
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $at, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
659
                }
660
                $node->setDestroyable($at);
661
            }
662
663
            return [
664
                'code' => 204,
665
            ];
666
        });
667
    }
668
669
    /**
670
     * @api {get} /api/v2/nodes/trash Get trash
671
     * @apiName getTrash
672
     * @apiVersion 2.0.0
673
     * @apiGroup Node
674
     * @apiPermission none
675
     * @apiDescription A similar endpoint to /api/v2/nodes/query filer={'deleted': {$type: 9}] but instead returning all deleted
676
     * nodes (including children which are deleted as well) this enpoint only returns the first deleted node from every subtree)
677
     * @apiUse _nodeAttributes
678
     *
679
     * @apiExample (cURL) example:
680
     * curl -XGET https://SERVER/api/v2/nodes/trash?pretty
681
     *
682
     * @apiParam (GET Parameter) {string[]} [attributes] Filter node attributes
683
     *
684
     * @apiSuccess (200 OK) {object[]} - List of deleted nodes
685
     * @apiSuccessExample {json} Success-Response:
686
     * HTTP/1.1 200 OK
687
     * [
688
     *  {
689
     *  }
690
     * ]
691
     */
692
    public function getTrash(array $attributes = [], int $offset = 0, int $limit = 20): Response
693
    {
694
        $children = [];
695
        $nodes = $this->fs->findNodesByFilterUser(NodeInterface::DELETED_ONLY, ['deleted' => ['$type' => 9]], $offset, $limit);
696
697
        foreach ($nodes as $node) {
698
            try {
699
                $parent = $node->getParent();
700
                if (null !== $parent && $parent->isDeleted()) {
701
                    continue;
702
                }
703
            } catch (\Exception $e) {
704
                //skip exception
705
            }
706
707
            $children[] = $node;
708
        }
709
710
        if ($this instanceof ApiFile) {
711
            $query['directory'] = false;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$query was never initialized. Although not strictly required by PHP, it is generally a good practice to add $query = 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...
712
            $uri = '/api/v2/files';
713
        } elseif ($this instanceof ApiCollection) {
714
            $query['directory'] = true;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$query was never initialized. Although not strictly required by PHP, it is generally a good practice to add $query = 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...
715
            $uri = '/api/v2/collections';
716
        } else {
717
            $uri = '/api/v2/nodes';
718
        }
719
720
        $pager = new Pager($this->node_decorator, $children, $attributes, $offset, $limit, $uri, $nodes->getReturn());
721
        $result = $pager->paging();
722
723
        return (new Response())->setCode(200)->setBody($result);
724
    }
725
726
    /**
727
     * @api {get} /api/v2/nodes/delta Get delta
728
     * @apiVersion 2.0.0
729
     * @apiName getDelta
730
     * @apiGroup Node
731
     * @apiPermission none
732
     * @apiUse _getNode
733
     *
734
     * @apiDescription Use this method to request a delta feed with all changes on the server (or) a snapshot of the server state.
735
     * since the state of the submited cursor. If no cursor was submited the server will create one which can than be used to request any further deltas.
736
     * If has_more is TRUE you need to request /delta immediatly again to
737
     * receive the next bunch of deltas. If has_more is FALSE you should wait at least 120s seconds before any further requests to the
738
     * api endpoint. You can also specify additional node attributes with the $attributes paramter or request the delta feed only for a specific node (see Get Attributes for that).
739
     * If reset is TRUE you have to clean your local state because you will receive a snapshot of the server state, it is the same as calling the /delta endpoint
740
     * without a cursor. reset could be TRUE if there was an account maintenance or a simialar case.
741
     * You can request a different limit as well but be aware that the number of nodes could be slighty different from your requested limit.
742
     * If requested with parameter id or p the delta gets generated recursively from the node given.
743
     *
744
     * @apiParam (GET Parameter) {number} [limit=250] Limit the number of delta entries, if too low you have to call this endpoint more often since has_more would be true more often
745
     * @apiParam (GET Parameter) {string[]} [attributes] Filter attributes, per default not all attributes would be returned
746
     * @apiParam (GET Parameter) {string} [cursor=null] Set a cursor to rquest next nodes within delta processing
747
     *
748
     * @apiExample (cURL) example:
749
     * curl -XGET "https://SERVER/api/v2/nodes/delta?pretty"
750
     *
751
     * @apiSuccess (200 OK) {boolean} reset If true the local state needs to be reseted, is alway TRUE during
752
     * the first request to /delta without a cursor or in special cases like server or account maintenance
753
     * @apiSuccess (200 OK) {string} cursor The cursor needs to be stored and reused to request further deltas
754
     * @apiSuccess (200 OK) {boolean} has_more If has_more is TRUE /delta can be requested immediatly after the last request
755
     * to receive further delta. If it is FALSE we should wait at least 120 seconds before any further delta requests to the api endpoint
756
     * @apiSuccess (200 OK) {object[]} nodes Node list to process
757
     * @apiSuccessExample {json} Success-Response:
758
     * HTTP/1.1 200 OK
759
     * {
760
     *      "reset": false,
761
     *      "cursor": "aW5pdGlhbHwxMDB8NTc1YTlhMGIzYzU4ODkwNTE0OGI0NTZifDU3NWE5YTBiM2M1ODg5MDUxNDhiNDU2Yw==",
762
     *      "has_more": false,
763
     *       "nodes": [
764
     *          {
765
     *              "id": "581afa783c5889ad7c8b4572",
766
     *              "deleted": " 2008-08-31T16:47+00:00",
767
     *              "changed": "2007-08-31T16:47+00:00",
768
     *              "path": "\/AAA\/AL",
769
     *              "directory": true
770
     *          },
771
     *          {
772
     *              "id": "581afa783c5889ad7c8b3dcf",
773
     *              "created": "2007-08-31T16:47+00:00",
774
     *              "changed": "2007-09-28T12:33+00:00",
775
     *              "path": "\/AL",
776
     *              "directory": true
777
     *          }
778
     *      ]
779
     * }
780
     *
781
     * @param string $id
782
     * @param string $p
783
     * @param string $cursor
784
     */
785
    public function getDelta(
786
        DeltaAttributeDecorator $delta_decorator,
787
        ?string $id = null,
788
        ?string $p = null,
789
        ?string $cursor = null,
790
        int $limit = 250,
791
        array $attributes = []
792
    ): Response {
793
        if (null !== $id || null !== $p) {
794
            $node = $this->_getNode($id, $p);
795
        } else {
796
            $node = null;
797
        }
798
799
        $result = $this->fs->getDelta()->getDeltaFeed($cursor, $limit, $node);
800
        foreach ($result['nodes'] as &$node) {
801
            if ($node instanceof NodeInterface) {
802
                $node = $this->node_decorator->decorate($node, $attributes);
803
            } else {
804
                $node = $delta_decorator->decorate($node, $attributes);
805
            }
806
        }
807
808
        return (new Response())->setCode(200)->setBody($result);
809
    }
810
811
    /**
812
     * @api {get} /api/v2/nodes/:id/event-log Event log
813
     * @apiVersion 2.0.0
814
     * @apiName getEventLog
815
     * @apiGroup Node
816
     * @apiPermission none
817
     * @apiUse _getNode
818
     * @apiDescription Get detailed event log
819
     * Request all modifications which are made by the user himself or share members.
820
     * Possible operations are the follwing:
821
     * - deleteCollectionReference
822
     * - deleteCollectionShare
823
     * - deleteCollection
824
     * - addCollection
825
     * - addFile
826
     * - addCollectionShare
827
     * - addCollectionReference
828
     * - undeleteFile
829
     * - undeleteCollectionReference
830
     * - undeleteCollectionShare
831
     * - restoreFile
832
     * - renameFile
833
     * - renameCollection
834
     * - renameCollectionShare
835
     * - renameCollectionRFeference
836
     * - copyFile
837
     * - copyCollection
838
     * - copyCollectionShare
839
     * - copyCollectionRFeference
840
     * - moveFile
841
     * - moveCollection
842
     * - moveCollectionReference
843
     * - moveCollectionShare
844
     *
845
     * @apiExample (cURL) example:
846
     * curl -XGET "https://SERVER/api/v2/nodes/event-log?pretty"
847
     * curl -XGET "https://SERVER/api/v2/nodes/event-log?id=544627ed3c58891f058b4686&pretty"
848
     * curl -XGET "https://SERVER/api/v2/nodes/544627ed3c58891f058b4686/event-log?pretty&limit=10"
849
     * curl -XGET "https://SERVER/api/v2/nodes/event-log?p=/absolute/path/to/my/node&pretty"
850
     *
851
     * @apiParam (GET Parameter) {number} [limit=100] Sets limit of events to be returned
852
     * @apiParam (GET Parameter) {number} [skip=0] How many events are skiped (useful for paging)
853
     *
854
     * @apiSuccess (200 OK) {object[]} - List of events
855
     * @apiSuccess (200 OK) {number} -.event Event ID
856
     * @apiSuccess (200 OK) {object} -.timestamp ISO8601 timestamp when the event occured
857
     * @apiSuccess (200 OK) {string} -.operation event operation (like addCollection, deleteFile, ...)
858
     * @apiSuccess (200 OK) {object} -.parent Parent node object at the time of the event
859
     * @apiSuccess (200 OK) {object} -.previous Previous state of actual data which has been modified during an event, can contain either version, name or parent
860
     * @apiSuccess (200 OK) {number} -.previous.version Version at the time before the event
861
     * @apiSuccess (200 OK) {string} -.previous.name Name at the time before the event
862
     * @apiSuccess (200 OK) {object} -.previous.parent Parent node object at the time before the event
863
     * @apiSuccess (200 OK) {object} -.share shared collection object at the time of the event (If the node was part of a share)
864
     * @apiSuccess (200 OK) {string} -.name Name of the node at the time of the event
865
     * @apiSuccess (200 OK) {object} -.node Current node object
866
     * @apiSuccess (200 OK) {object} -.user User who executed an event
867
     *
868
     * @apiSuccessExample {json} Success-Response:
869
     * HTTP/1.1 200 OK
870
     * [
871
     *  {
872
     *      "id": "57628e523c5889026f8b4570",
873
     *      "timestamp": " 2018-01-02T13:22+00:00",
874
     *      "operation": "restoreFile",
875
     *      "name": "file.txt",
876
     *      "previous": {
877
     *          "version": 16
878
     *      },
879
     *      "node": {
880
     *          "id": "558c0b273c588963078b457a",
881
     *          "name": "3dddsceheckfile.txt",
882
     *          "deleted": false
883
     *      },
884
     *      "parent": null,
885
     *      "user": {
886
     *          "id": "54354cb63c58891f058b457f",
887
     *          "username": "example"
888
     *      }
889
     *  }
890
     * ]
891
     *
892
     * @param string $id
893
     * @param string $p
894
     */
895
    public function getEventLog(EventAttributeDecorator $event_decorator, ?string $id = null, ?string $p = null, ?array $attributes = [], int $offset = 0, int $limit = 20): Response
896
    {
897
        if (null !== $id || null !== $p) {
898
            $node = $this->_getNode($id, $p);
899
            $uri = '/api/v2/nodes/'.$node->getId().'/event-log';
900
        } else {
901
            $node = null;
902
            $uri = '/api/v2/nodes/event-log';
903
        }
904
905
        $result = $this->fs->getDelta()->getEventLog($limit, $offset, $node, $total);
906
        $pager = new Pager($event_decorator, $result, $attributes, $offset, $limit, $uri, $total);
907
908
        return (new Response())->setCode(200)->setBody($pager->paging());
909
    }
910
911
    /**
912
     * @api {get} /api/v2/nodes/last-cursor Get last Cursor
913
     * @apiVersion 2.0.0
914
     * @apiName geLastCursor
915
     * @apiGroup Node
916
     * @apiUse _getNode
917
     * @apiPermission none
918
     * @apiDescription Use this method to request the latest cursor if you only need to now
919
     * if there are changes on the server. This method will not return any other data than the
920
     * newest cursor. To request a feed with all deltas request /delta.
921
     *
922
     * @apiExample (cURL) example:
923
     * curl -XGET "https://SERVER/api/v2/nodes/last-cursor?pretty"
924
     *
925
     * @apiSuccess (200 OK) {string} cursor v2 cursor
926
     * @apiSuccessExample {json} Success-Response:
927
     * HTTP/1.1 200 OK
928
     * "aW5pdGlhbHwxMDB8NTc1YTlhMGIzYzU4ODkwNTE0OGI0NTZifDU3NWE5YTBiM2M1ODg5MDUxNDhiNDU2Yw=="
929
     *
930
     * @param string $id
931
     * @param string $p
932
     */
933
    public function getLastCursor(?string $id = null, ?string $p = null): Response
934
    {
935
        if (null !== $id || null !== $p) {
936
            $node = $this->_getNode($id, $p);
0 ignored issues
show
Unused Code introduced by
$node 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...
937
        } else {
938
            $node = null;
0 ignored issues
show
Unused Code introduced by
$node 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...
939
        }
940
941
        $result = $this->fs->getDelta()->getLastCursor();
942
943
        return (new Response())->setCode(200)->setBody($result);
944
    }
945
946
    /**
947
     * Merge multiple nodes into one zip archive.
948
     *
949
     * @param string $id
950
     * @param string $path
951
     */
952
    protected function combine($id = null, $path = null, string $name = 'selected')
953
    {
954
        $archive = new ZipStream($name.'.zip');
955
956
        foreach ($this->_getNodes($id, $path) as $node) {
957
            try {
958
                $node->zip($archive);
959
            } catch (\Exception $e) {
960
                $this->logger->debug('failed zip node in multi node request ['.$node->getId().']', [
961
                   'category' => get_class($this),
962
                   'exception' => $e,
963
               ]);
964
            }
965
        }
966
967
        $archive->finish();
968
    }
969
970
    /**
971
     * Check custom node attributes which have to be written.
972
     */
973
    protected function _verifyAttributes(array $attributes): array
974
    {
975
        $valid_attributes = [
976
            'changed',
977
            'destroy',
978
            'created',
979
            'meta',
980
            'readonly',
981
            'acl',
982
        ];
983
984
        if ($this instanceof ApiCollection) {
985
            $valid_attributes += ['filter', 'mount'];
986
        }
987
988
        $check = array_merge(array_flip($valid_attributes), $attributes);
989
990
        if ($this instanceof ApiCollection && count($check) > 8) {
991
            throw new Exception\InvalidArgument('Only changed, created, destroy timestamp, acl, filter, mount, readonly and/or meta attributes may be overwritten');
992
        }
993
        if ($this instanceof ApiFile && count($check) > 6) {
994
            throw new Exception\InvalidArgument('Only changed, created, destroy timestamp, acl, readonly and/or meta attributes may be overwritten');
995
        }
996
997
        foreach ($attributes as $attribute => $value) {
998
            switch ($attribute) {
999
                case 'filter':
1000
                    if (!is_array($value)) {
1001
                        throw new Exception\InvalidArgument($attribute.' must be an array');
1002
                    }
1003
1004
                    $attributes['filter'] = json_encode($value);
1005
1006
                break;
1007
                case 'destroy':
1008
                    if (!Helper::isValidTimestamp($value)) {
1009
                        throw new Exception\InvalidArgument($attribute.' timestamp must be valid unix timestamp');
1010
                    }
1011
                    $attributes[$attribute] = new UTCDateTime($value.'000');
1012
1013
                break;
1014
                case 'changed':
1015
                case 'created':
1016
                    if (!Helper::isValidTimestamp($value)) {
1017
                        throw new Exception\InvalidArgument($attribute.' timestamp must be valid unix timestamp');
1018
                    }
1019
                    if ((int) $value > time()) {
1020
                        throw new Exception\InvalidArgument($attribute.' timestamp can not be set greater than the server time');
1021
                    }
1022
                    $attributes[$attribute] = new UTCDateTime($value.'000');
1023
1024
                break;
1025
                case 'readonly':
1026
                    $attributes['readonly'] = (bool) $attributes['readonly'];
1027
1028
                break;
1029
            }
1030
        }
1031
1032
        return $attributes;
1033
    }
1034
}
1035