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

src/app/Balloon.App.Api/v1/Collection.php (1 issue)

Check for conflicting imported classes with local classes

Bug Major

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());
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();
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