Completed
Push — master ( 287393...7ff50d )
by Raffael
18:27 queued 14:12
created

src/app/Balloon.App.Api/v1/Collection.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * balloon
7
 *
8
 * @copyright   Copryright (c) 2012-2019 gyselroth GmbH (https://gyselroth.com)
9
 * @license     GPL-3.0 https://opensource.org/licenses/GPL-3.0
10
 */
11
12
namespace Balloon\App\Api\v1;
13
14
use Balloon\App\Api\v1\AttributeDecorator\RoleDecorator;
15
use Balloon\Filesystem\Exception;
16
use Balloon\Filesystem\Node\Collection as NodeCollection;
17
use Balloon\Server\User;
0 ignored issues
show
This use statement conflicts with another class in this namespace, Balloon\App\Api\v1\User.

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...
18
use Micro\Http\Response;
19
20
class Collection extends Node
21
{
22
    /**
23
     * @api {head} /api/v1/collection/children?id=:id children exists?
24
     * @apiVersion 1.0.0
25
     * @apiName head
26
     * @apiGroup Node\Collection
27
     * @apiPermission none
28
     * @apiDescription Check if collection has any children
29
     * @apiUse _getNode
30
     *
31
     * @apiExample (cURL) example:
32
     * curl -XHEAD "https://SERVER/api/v1/collection/children?id=544627ed3c58891f058b4686"
33
     * curl -XHEAD "https://SERVER/api/v1/collection/544627ed3c58891f058b4686/children"
34
     * curl -XHEAD "https://SERVER/api/v1/collection/children?p=/absolute/path/to/my/collection"
35
     *
36
     * @apiSuccessExample {json} Success-Response (Children exists):
37
     * HTTP/1.1 204 Not Content
38
     *
39
     * @apiErrorExample {json} Error-Response (No children exists):
40
     * HTTP/1.1 404 Not Found
41
     *
42
     * @param string $id
43
     * @param string $p
44
     */
45
    public function headChildren(?string $id = null, ?string $p = null): Response
46
    {
47
        $result = $this->fs->getNode($id, $p, null, false, true);
48
        $children = $result->getSize();
49
50
        $response = (new Response())
51
            ->setHeader('Content-Length', $children);
52
53
        if ($children > 0) {
54
            $response->setCode(204);
55
        } else {
56
            $response->setCode(404);
57
        }
58
59
        return $response;
60
    }
61
62
    /**
63
     * @api {get} /api/v1/collection/children Get children
64
     * @apiVersion 1.0.0
65
     * @apiName getChildren
66
     * @apiGroup Node\Collection
67
     * @apiPermission none
68
     * @apiDescription Find all children of a collection
69
     * @apiUse _getNode
70
     * @apiUse _nodeAttributes
71
     *
72
     * @apiExample (cURL) example:
73
     * curl -XGET "https://SERVER/api/v1/collection/children?id=212323eeffe2322344224452&pretty"
74
     * curl -XGET "https://SERVER/api/v1/collection/212323eeffe2322344224452/children?pretty&deleted=0"
75
     * curl -XGET "https://SERVER/api/v1/collection/children?p=/absolute/path/to/my/collection&deleted=1"
76
     *
77
     * @apiParam (GET Parameter) {string[]} [attributes] Filter node attributes
78
     * @apiParam (GET Parameter) {string[]} [filter] Filter nodes
79
     * @apiParam (GET Parameter) {number} [deleted=0] Wherever include deleted nodes or not, possible values:</br>
80
     * - 0 Exclude deleted</br>
81
     * - 1 Only deleted</br>
82
     * - 2 Include deleted</br>
83
     *
84
     * @apiSuccess (200 OK) {number} status Status Code
85
     * @apiSuccess (200 OK) {object[]} data Children
86
     * @apiSuccessExample {json} Success-Response:
87
     * HTTP/1.1 200 OK
88
     * {
89
     *      "status":200,
90
     *      "data": [{..}, {...}] //Shorted
91
     * }
92
     *
93
     * @param string $id
94
     * @param string $p
95
     */
96
    public function getChildren(
97
        ?string $id = null,
98
        ?string $p = null,
99
        int $deleted = 0,
100
        array $filter = [],
101
        array $attributes = []
102
    ): Response {
103
        $children = [];
104
        $nodes = $this->fs->getNode($id, $p, NodeCollection::class, false, true)->getChildNodes($deleted, $filter);
105
106
        foreach ($nodes as $node) {
107
            $children[] = $this->node_decorator->decorate($node, $attributes);
108
        }
109
110
        return (new Response())->setCode(200)->setBody([
111
            'status' => 200,
112
            'data' => $children,
113
        ]);
114
    }
115
116
    /**
117
     * @api {get} /api/v1/collection/share?id=:id Get Share settings
118
     * @apiVersion 1.0.0
119
     * @apiName getShare
120
     * @apiGroup Node\Collection
121
     * @apiPermission none
122
     * @apiDescription Get share acl and share name
123
     * @apiUse _getNode
124
     *
125
     * @apiExample (cURL) example:
126
     * curl -XGET "https://SERVER/api/v1/collection/share?id=212323eeffe2322344224452&pretty"
127
     * curl -XGET "https://SERVER/api/v1/collection/212323eeffe2322344224452/share?pretty"
128
     * curl -XGET "https://SERVER/api/v1/collection/share?p=/absolute/path/to/my/collection&pretty"
129
     *
130
     * @apiSuccess (200 OK) {number} status Status Code
131
     * @apiSuccess (200 OK) {string} data.name Share name
132
     * @apiSuccess (200 OK) {object[]} data.acl ACL rules
133
     * @apiSuccess (200 OK) {string} data.acl.type Either group or user
134
     * @apiSuccess (200 OK) {object} data.acl.role Role attributes
135
     * @apiSuccess (200 OK) {string} data.acl.priv Permission to access share
136
     * @apiSuccessExample {json} Success-Response:
137
     * HTTP/1.1 200 OK
138
     * {
139
     *      "status":200,
140
     *      "data":{
141
     *          "name": "my share",
142
     *          "acl": [
143
     *              {
144
     *                  "type":"user",
145
     *                  "role": {
146
     *                      "id": "212323eeffe2322355224411",
147
     *                  },
148
     *                  "privilege":"rw"
149
     *              }
150
     *          ]
151
     *      }
152
     *}
153
     *
154
     * @param string $id
155
     * @param string $p
156
     */
157
    public function getShare(RoleDecorator $role_decorator, ?string $id = null, ?string $p = null, array $attributes = []): Response
158
    {
159
        $node = $this->fs->getNode($id, $p);
160
        $rules = false;
161
162
        if ($node->isShare()) {
163
            $rules = [];
164
            $acl = $node->getAcl();
165
166
            foreach ($acl as &$rule) {
167
                $role = $role_decorator->decorate($rule['role'], $attributes);
168
                $rules[] = [
169
                    'type' => $rule['role'] instanceof User ? 'user' : 'group',
170
                    'priv' => $rule['privilege'],
171
                    'name' => $role['name'],
172
                    'id' => (string) $rule['id'],
173
                ];
174
            }
175
        }
176
177
        return (new Response())->setCode(200)->setBody([
178
            'status' => 200,
179
            'data' => $rules,
180
        ]);
181
    }
182
183
    /**
184
     * @api {post} /api/v1/collection/share?id=:id Create share
185
     * @apiVersion 1.0.0
186
     * @apiGroup Node\Collection
187
     * @apiPermission none
188
     * @apiDescription Create a new share from an existing collection
189
     * @apiUse _getNode
190
     * @apiUse _writeAction
191
     *
192
     * @apiExample (cURL) example:
193
     * curl -XPOST "https://SERVER/api/v1/collection/share?id=212323eeffe2322344224452&pretty"
194
     *
195
     * @apiParam (POST Parameter) {object[]} acl ACL rules
196
     * @apiParam (POST Parameter) {string} acl.type user or group
197
     * @apiParam (POST Parameter) {string} acl.role Role id (user or group id)
198
     * @apiParam (POST Parameter) {string} acl.privilege Permission to access share, could be on of the following:</br>
199
     *  rw - READ/WRITE </br>
200
     *  r - READONLY </br>
201
     *  w+ - INBOX (Only Access to owned nodes) </br>
202
     *  m - Manage </br>
203
     *  d - DENY </br>
204
     *
205
     * @apiSuccess (201 Created) {number} status Status code
206
     * @apiSuccess (201 Created) {boolean} data
207
     * @apiSuccessExample {json} Success-Response (Created or Modified Share):
208
     * HTTP/1.1 201 Created
209
     * {
210
     *      "status":201,
211
     *      "data": true
212
     * }
213
     *
214
     * @apiSuccessExample {json} Success-Response (Removed share):
215
     * HTTP/1.1 204 No Content
216
     *
217
     * @param string $id
218
     * @param string $p
219
     */
220
    public function postShare(array $acl, ?string $id = null, ?string $p = null): Response
221
    {
222
        $rules = [];
223
        foreach ($acl as $type => $ruleset) {
224
            foreach ($ruleset as $rule) {
225
                $rules[] = [
226
                    'type' => $type,
227
                    'id' => $rule[$type],
228
                    'privilege' => $rule['priv'],
229
                ];
230
            }
231
        }
232
233
        $node = $this->fs->getNode($id, $p);
234
        $result = $node->share($rules, $node->getName());
0 ignored issues
show
Consider using $node->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
235
236
        if (null === $result) {
237
            return (new Response())->setCode(204);
238
        }
239
240
        return (new Response())->setCode(201)->setBody([
241
            'status' => 201,
242
            'data' => $result,
243
        ]);
244
    }
245
246
    /**
247
     * @api {delete} /api/v1/collection/share?id=:id Delete share
248
     * @apiVersion 1.0.0
249
     * @apiName deleteShare
250
     * @apiGroup Node\Collection
251
     * @apiPermission none
252
     * @apiDescription Does only remove sharing options and transform a share back into a normal collection.
253
     * There won't be any data loss after this action. All existing references would be removed automatically.
254
     * @apiUse _getNode
255
     * @apiUse _writeAction
256
     *
257
     * @apiExample (cURL) example:
258
     * curl -XDELETE "https://SERVER/api/v1/collection/share?id=212323eeffe2322344224452"
259
     * curl -XDELETE "https://SERVER/api/v1/collection/212323eeffe2322344224452/share"
260
     * curl -XDELETE "https://SERVER/api/v1/collection/share?p=/absolute/path/to/my/collection"
261
     *
262
     * @apiSuccessExample {json} Success-Response:
263
     * HTTP/1.1 204 No Content
264
     *
265
     * @param string $id
266
     * @param string $p
267
     */
268
    public function deleteShare(?string $id = null, ?string $p = null): Response
269
    {
270
        $node = $this->fs->getNode($id, $p);
271
        $result = $node->unshare();
0 ignored issues
show
$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...
272
273
        return (new Response())->setCode(204);
274
    }
275
276
    /**
277
     * @api {post} /api/v1/collection?id=:id Create collection
278
     * @apiVersion 1.0.0
279
     * @apiName post
280
     * @apiGroup Node\Collection
281
     * @apiPermission none
282
     * @apiDescription Create a new collection. You can create a new collection combining a parent collection (id) and a name (name)
283
     * or set an absolute path (p) to the new collection. Additionally it is possible to overwrite server generated timestamps like created or changed (attributes).
284
     * Via the more advanced option filter (attributes.filter) you can create a special collection which can contain any nodes based on the given filter.
285
     * For example a filter could be {mime: application/pdf}, therefore the collection would contain all files with mimetype application/pdf accessible by you.
286
     * (Attention this can result in a slow server response since you could create a filter where no indexes exists, therefore the database engine needs to search the entire database)
287
     * @apiUse _getNode
288
     * @apiUse _conflictNode
289
     * @apiUse _writeAction
290
     *
291
     * @apiExample (cURL) example:
292
     * curl -XGET "https://SERVER/api/v1/collection?id=544627ef3c58891f058b468f&name=MyNewFolder&pretty"
293
     * curl -XGET "https://SERVER/api/v1/collection/544627ef3c58891f058b468f?name=MyNewFolder&pretty"
294
     * curl -XGET "https://SERVER/api/v1/collection/?p=/absolute/path/to/my/collection&name=MyNewFolder&pretty&conflict=2"
295
     *
296
     * @apiParam (GET Parameter) {string} id Either id or p (path) of a node must be given.
297
     * @apiParam (GET Parameter) {string} p Either id or p (path) of a node must be given. If a path is given, no name must be set,
298
     * the path must contain the name of the new collection.
299
     * @apiParam (GET Parameter) {string} name A collection name must be set in conjuction with id, don't need to set with a path
300
     * @apiParam (GET Parameter) {object} attributes Overwrite some attributes which are usually generated on the server
301
     * @apiParam (GET Parameter) {number} attributes.created Set specific created timestamp (UNIX timestamp format)
302
     * @apiParam (GET Parameter) {number} attributes.changed Set specific changed timestamp (UNIX timestamp format)
303
     * @apiParam (GET Parameter) {number} attributes.destroy Set specific self-destroy timestamp (UNIX timestamp format)
304
     * @apiParam (GET Parameter) {array} attributes.filter Set specific set of children instead just parent=this
305
     *
306
     * @apiSuccess (201 Created) {number} status Status Code
307
     * @apiSuccess (201 Created) {string} data Node ID
308
     * @apiSuccessExample {json} Success-Response:
309
     * HTTP/1.1 201 Created
310
     * {
311
     *      "status":201,
312
     *      "data": "544627ed3c58891f058b4682"
313
     * }
314
     *
315
     * @param string $id
316
     * @param string $p
317
     */
318
    public function post(
319
        ?string $id = null,
320
        ?string $p = null,
321
        ?string $name = null,
322
        array $attributes = [],
323
        int $conflict = 0
324
    ): Response {
325
        if (null !== $p && null !== $name) {
326
            throw new Exception\InvalidArgument('p and name can not be used at the same time');
327
        }
328
329
        $attributes = $this->_verifyAttributes($attributes);
330
331
        if (null === $id && null !== $p) {
332
            if (!is_string($p) || empty($p)) {
333
                throw new Exception\InvalidArgument('name must be a valid string');
334
            }
335
336
            $parent_path = dirname($p);
337
            $name = basename($p);
338
            $parent = $this->fs->findNodeByPath($parent_path, NodeCollection::class);
339
            $result = $parent->addDirectory($name, $attributes, $conflict)->getId();
340
341
            return (new Response())->setCode(201)->setBody([
342
                'status' => 201,
343
                'data' => (string) $result,
344
            ]);
345
        }
346
347
        if (null !== $id && null === $name) {
348
            throw new Exception\InvalidArgument('name must be set with id');
349
        }
350
351
        $parent = $this->fs->getNode($id, null, null, false, true);
352
        $result = $parent->addDirectory((string) $name, $attributes, $conflict)->getId();
353
354
        return (new Response())->setCode(201)->setBody([
355
            'status' => 201,
356
            'data' => (string) $result,
357
        ]);
358
    }
359
}
360