Completed
Push — master ( c6728e...bf498d )
by Raffael
14:18 queued 04:37
created

Collection::addFile()   D

Complexity

Conditions 11
Paths 258

Size

Total Lines 90

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
dl 0
loc 90
ccs 0
cts 50
cp 0
rs 4.8248
c 0
b 0
f 0
cc 11
nc 258
nop 5
crap 132

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Filesystem\Node;
13
14
use Balloon\Filesystem;
15
use Balloon\Filesystem\Acl;
16
use Balloon\Filesystem\Acl\Exception\Forbidden as ForbiddenException;
17
use Balloon\Filesystem\Exception;
18
use Balloon\Filesystem\Storage;
19
use Balloon\Hook;
20
use Balloon\Server\User;
21
use Generator;
22
use MongoDB\BSON\ObjectId;
23
use MongoDB\BSON\Regex;
24
use MongoDB\BSON\UTCDateTime;
25
use Psr\Log\LoggerInterface;
26
use Sabre\DAV\IQuota;
27
28
class Collection extends AbstractNode implements IQuota
29
{
30
    /**
31
     * Root folder.
32
     */
33
    const ROOT_FOLDER = '/';
34
35
    /**
36
     * Share acl.
37
     *
38
     * @var array
39
     */
40
    protected $acl = [];
41
42
    /**
43
     * Share name.
44
     *
45
     * @var string
46
     */
47
    protected $share_name;
48
49
    /**
50
     * filter.
51
     *
52
     * @param string
53
     */
54
    protected $filter;
55
56
    /**
57
     * Initialize.
58
     */
59
    public function __construct(array $attributes, Filesystem $fs, LoggerInterface $logger, Hook $hook, Acl $acl, Storage $storage)
60
    {
61
        $this->_fs = $fs;
62
        $this->_server = $fs->getServer();
63
        $this->_db = $fs->getDatabase();
64
        $this->_user = $fs->getUser();
65
        $this->_logger = $logger;
66
        $this->_hook = $hook;
67
        $this->_acl = $acl;
68
        $this->_storage = $storage;
69
70
        foreach ($attributes as $attr => $value) {
71
            $this->{$attr} = $value;
72
        }
73
74
        $this->mime = 'inode/directory';
75
        $this->raw_attributes = $attributes;
76
    }
77
78
    /**
79
     * Copy node with children.
80
     *
81
     * @param Collection $parent
82
     * @param string     $recursion
83
     */
84
    public function copyTo(self $parent, int $conflict = NodeInterface::CONFLICT_NOACTION, ?string $recursion = null, bool $recursion_first = true): NodeInterface
85
    {
86
        if (null === $recursion) {
87
            $recursion_first = true;
88
            $recursion = uniqid();
89
        } else {
90
            $recursion_first = false;
91
        }
92
93
        $this->_hook->run(
94
            'preCopyCollection',
95
            [$this, $parent, &$conflict, &$recursion, &$recursion_first]
96
        );
97
98
        if (NodeInterface::CONFLICT_RENAME === $conflict && $parent->childExists($this->name)) {
99
            $name = $this->getDuplicateName();
100
        } else {
101
            $name = $this->name;
102
        }
103
104
        if ($this->_id === $parent->getId()) {
105
            throw new Exception\Conflict(
106
                'can not copy node into itself',
107
                Exception\Conflict::CANT_COPY_INTO_ITSELF
108
            );
109
        }
110
111
        if (NodeInterface::CONFLICT_MERGE === $conflict && $parent->childExists($this->name)) {
112
            $new_parent = $parent->getChild($this->name);
113
        } else {
114
            $new_parent = $parent->addDirectory($name, [
115
                'created' => $this->created,
116
                'changed' => $this->changed,
117
                'deleted' => $this->deleted,
118
                'filter' => $this->filter,
119
                'meta' => $this->meta,
120
            ], NodeInterface::CONFLICT_NOACTION, true);
121
        }
122
123
        foreach ($this->getChildNodes(NodeInterface::DELETED_INCLUDE) as $child) {
124
            $child->copyTo($new_parent, $conflict, $recursion, false);
125
        }
126
127
        $this->_hook->run(
128
            'postCopyCollection',
129
            [$this, $parent, $new_parent, $conflict, $recursion, $recursion_first]
130
        );
131
132
        return $new_parent;
133
    }
134
135
    /**
136
     * Get Share name.
137
     */
138
    public function getShareName(): string
139
    {
140
        if ($this->isShare()) {
141
            return $this->share_name;
142
        }
143
144
        return $this->_fs->findRawNode($this->getShareId())['share_name'];
0 ignored issues
show
Bug introduced by
It seems like $this->getShareId() can be null; however, findRawNode() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
145
    }
146
147
    /**
148
     * Get Attributes.
149
     */
150
    public function getAttributes(): array
151
    {
152
        return [
153
            '_id' => $this->_id,
154
            'name' => $this->name,
155
            'shared' => $this->shared,
156
            'share_name' => $this->share_name,
157
            'acl' => $this->acl,
158
            'directory' => true,
159
            'reference' => $this->reference,
160
            'parent' => $this->parent,
161
            'app' => $this->app,
162
            'owner' => $this->owner,
163
            'meta' => $this->meta,
164
            'mime' => $this->mime,
165
            'filter' => $this->filter,
166
            'deleted' => $this->deleted,
167
            'changed' => $this->changed,
168
            'created' => $this->created,
169
            'destroy' => $this->destroy,
170
            'readonly' => $this->readonly,
171
        ];
172
    }
173
174
    /**
175
     * Set collection filter.
176
     *
177
     * @param string $filter
178
     */
179
    public function setFilter(?array $filter = null): bool
180
    {
181
        $this->filter = json_encode($filter);
182
183
        return $this->save('filter');
184
    }
185
186
    /**
187
     * Get collection.
188
     */
189
    public function get(): void
190
    {
191
        $this->getZip();
192
    }
193
194
    /**
195
     * Fetch children items of this collection.
196
     *
197
     * Deleted:
198
     *  0 - Exclude deleted
199
     *  1 - Only deleted
200
     *  2 - Include deleted
201
     *
202
     * @param int $offset
203
     * @param int $limit
204
     */
205
    public function getChildNodes(int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = [], ?int $offset = null, ?int $limit = null): Generator
206
    {
207
        $filter = $this->getChildrenFilter($deleted, $filter);
208
209
        return $this->_fs->findNodesByFilter($filter, $offset, $limit);
210
    }
211
212
    /**
213
     * Fetch children items of this collection (as array).
214
     *
215
     * Deleted:
216
     *  0 - Exclude deleted
217
     *  1 - Only deleted
218
     *  2 - Include deleted
219
     */
220
    public function getChildren(int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): array
221
    {
222
        return iterator_to_array($this->getChildNodes($deleted, $filter));
223
    }
224
225
    /**
226
     * Is custom filter node.
227
     */
228
    public function isCustomFilter(): bool
229
    {
230
        return !empty($this->filter);
231
    }
232
233
    /**
234
     * Get number of children.
235
     */
236
    public function getSize(): int
237
    {
238
        return $this->_db->storage->count($this->getChildrenFilter());
239
    }
240
241
    /**
242
     * Get real id (reference).
243
     *
244
     * @return ObjectId
245
     */
246
    public function getRealId(): ?ObjectId
247
    {
248
        if (true === $this->shared && $this->isReference()) {
249
            return $this->reference;
250
        }
251
252
        return $this->_id;
253
    }
254
255
    /**
256
     * Get user quota information.
257
     */
258
    public function getQuotaInfo(): array
259
    {
260
        $quota = $this->_user->getQuotaUsage();
261
262
        return [
263
            $quota['used'],
264
            $quota['available'],
265
        ];
266
    }
267
268
    /**
269
     * Fetch children items of this collection.
270
     */
271
    public function getChild($name, int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): NodeInterface
272
    {
273
        $filter = $this->getChildrenFilter($deleted, $filter);
274
        $filter['name'] = new Regex('^'.preg_quote($name).'$', 'i');
275
        $node = $this->_db->storage->findOne($filter);
276
277
        if (null === $node) {
278
            throw new Exception\NotFound(
279
                'node called '.$name.' does not exists here',
280
                Exception\NotFound::NODE_NOT_FOUND
281
            );
282
        }
283
284
        $this->_logger->debug('loaded node ['.$node['_id'].' from parent node ['.$this->getRealId().']', [
285
            'category' => get_class($this),
286
        ]);
287
288
        return $this->_fs->initNode($node);
289
    }
290
291
    /**
292
     * Delete node.
293
     *
294
     * Actually the node will not be deleted (Just set a delete flag), set $force=true to
295
     * delete finally
296
     *
297
     * @param string $recursion Identifier to identify a recursive action
298
     */
299
    public function delete(bool $force = false, ?string $recursion = null, bool $recursion_first = true): bool
300
    {
301
        if (!$this->isReference() && !$this->_acl->isAllowed($this, 'w')) {
302
            throw new ForbiddenException(
303
                'not allowed to delete node '.$this->name,
304
                ForbiddenException::NOT_ALLOWED_TO_DELETE
305
            );
306
        }
307
308
        if (null === $recursion) {
309
            $recursion_first = true;
310
            $recursion = uniqid();
311
        } else {
312
            $recursion_first = false;
313
        }
314
315
        $this->_hook->run(
316
            'preDeleteCollection',
317
            [$this, &$force, &$recursion, &$recursion_first]
318
        );
319
320
        if (true === $force) {
321
            return $this->_forceDelete($recursion, $recursion_first);
322
        }
323
324
        $this->deleted = new UTCDateTime();
325
326
        if (!$this->isReference()) {
327
            $this->doRecursiveAction(function ($node) use ($recursion) {
328
                $node->delete(false, $recursion, false);
329
            }, NodeInterface::DELETED_EXCLUDE);
330
        }
331
332
        if (null !== $this->_id) {
333
            $result = $this->save([
334
                'deleted',
335
            ], [], $recursion, false);
336
        } else {
337
            $result = true;
338
        }
339
340
        $this->_hook->run(
341
            'postDeleteCollection',
342
            [$this, $force, $recursion, $recursion_first]
343
        );
344
345
        return $result;
346
    }
347
348
    /**
349
     * Check if this collection has child named $name.
350
     *
351
     * deleted:
352
     *
353
     *  0 - Exclude deleted
354
     *  1 - Only deleted
355
     *  2 - Include deleted
356
     *
357
     * @param string $name
358
     * @param int    $deleted
359
     */
360
    public function childExists($name, $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): bool
361
    {
362
        $find = [
363
            'parent' => $this->getRealId(),
364
            'name' => new Regex('^'.preg_quote($name).'$', 'i'),
365
        ];
366
367
        if (null !== $this->_user) {
368
            $find['owner'] = $this->_user->getId();
369
        }
370
371
        switch ($deleted) {
372
            case NodeInterface::DELETED_EXCLUDE:
373
                $find['deleted'] = false;
374
375
                break;
376
            case NodeInterface::DELETED_ONLY:
377
                $find['deleted'] = ['$type' => 9];
378
379
                break;
380
        }
381
382
        $find = array_merge($filter, $find);
383
384
        if ($this->isSpecial()) {
385
            unset($find['owner']);
386
        }
387
388
        $node = $this->_db->storage->findOne($find);
389
390
        return (bool) $node;
391
    }
392
393
    /**
394
     * Share collection.
395
     */
396
    public function share(array $acl, string $name): bool
397
    {
398
        if ($this->isShareMember()) {
399
            throw new Exception('a sub node of a share can not be shared');
400
        }
401
402
        if (!$this->_acl->isAllowed($this, 'm')) {
403
            throw new ForbiddenException(
404
                'not allowed to share node',
405
                ForbiddenException::NOT_ALLOWED_TO_MANAGE
406
            );
407
        }
408
409
        $this->_acl->validateAcl($this->_server, $acl);
410
411
        $action = [
412
            '$set' => [
413
                'shared' => $this->getRealId(),
414
            ],
415
        ];
416
417
        $toset = $this->getChildrenRecursive($this->getRealId(), $shares);
418
419
        if (!empty($shares)) {
420
            throw new Exception('child folder contains a shared folder');
421
        }
422
423
        $this->_db->storage->updateMany([
424
            '_id' => [
425
                '$in' => $toset,
426
            ],
427
        ], $action);
428
429
        $this->_db->delta->updateMany([
430
            '_id' => [
431
                '$in' => $toset,
432
            ],
433
        ], $action);
434
435
        if ($this->getRealId() === $this->_id) {
436
            $this->acl = $acl;
437
            $this->shared = true;
438
            $this->share_name = $name;
439
            $this->save(['acl', 'shared', 'share_name']);
440
        } else {
441
            $this->_db->storage->updateOne([
442
                '_id' => $this->getRealId(),
443
            ], [
444
                '$set' => [
445
                    'share_name' => $name,
446
                    'acl' => $acl,
447
                ],
448
            ]);
449
        }
450
451
        return true;
452
    }
453
454
    /**
455
     * Unshare collection.
456
     */
457
    public function unshare(): bool
458
    {
459
        if (!$this->_acl->isAllowed($this, 'm')) {
460
            throw new ForbiddenException(
461
                'not allowed to share node',
462
                ForbiddenException::NOT_ALLOWED_TO_MANAGE
463
            );
464
        }
465
466
        if (true !== $this->shared) {
467
            throw new Exception\Conflict(
468
                'Can not unshare a none shared collection',
469
                Exception\Conflict::NOT_SHARED
470
            );
471
        }
472
473
        $this->shared = false;
474
        $this->share_name = null;
475
        $this->acl = [];
476
        $action = [
477
            '$unset' => [
478
                'shared' => $this->_id,
479
            ],
480
            '$set' => [
481
                'owner' => $this->_user->getId(),
482
            ],
483
        ];
484
485
        $toset = $this->getChildrenRecursive($this->getRealId(), $shares);
486
487
        $this->_db->storage->updateMany([
488
            '_id' => [
489
                '$in' => $toset,
490
            ],
491
        ], $action);
492
493
        $result = $this->save(['shared'], ['acl', 'share_name']);
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...
494
495
        return true;
496
    }
497
498
    /**
499
     * Get children.
500
     *
501
     * @param ObjectId $id
502
     * @param array    $shares
503
     */
504
    public function getChildrenRecursive(?ObjectId $id = null, ?array &$shares = []): array
505
    {
506
        $list = [];
507
        $result = $this->_db->storage->find([
508
            'parent' => $id,
509
        ], [
510
            '_id' => 1,
511
            'directory' => 1,
512
            'reference' => 1,
513
            'shared' => 1,
514
        ]);
515
516
        foreach ($result as $node) {
517
            $list[] = $node['_id'];
518
519
            if ($node['directory'] === true) {
520
                if (isset($node['reference']) || isset($node['shared']) && true === $node['shared']) {
521
                    $shares[] = $node['_id'];
522
                }
523
524
                if (true === $node['directory'] && !isset($node['reference'])) {
525
                    $list = array_merge($list, $this->getChildrenRecursive($node['_id'], $shares));
526
                }
527
            }
528
        }
529
530
        return $list;
531
    }
532
533
    /**
534
     * Create new directory.
535
     *
536
     * @param string $name
537
     * @param arracy $attributes
538
     *
539
     * @return Collection
540
     */
541
    public function addDirectory($name, array $attributes = [], int $conflict = NodeInterface::CONFLICT_NOACTION, bool $clone = false): self
542
    {
543
        if (!$this->_acl->isAllowed($this, 'w')) {
544
            throw new ForbiddenException(
545
                'not allowed to create new node here',
546
                ForbiddenException::NOT_ALLOWED_TO_CREATE
547
            );
548
        }
549
550
        $this->_hook->run('preCreateCollection', [$this, &$name, &$attributes, &$clone]);
551
552
        if ($this->readonly) {
553
            throw new Exception\Conflict(
554
                'node is set as readonly, it is not possible to add new sub nodes',
555
                Exception\Conflict::READONLY
556
            );
557
        }
558
559
        $name = $this->checkName($name);
560
561
        if ($this->childExists($name)) {
562
            if (NodeInterface::CONFLICT_NOACTION === $conflict) {
563
                throw new Exception\Conflict(
564
                    'a node called '.$name.' does already exists in this collection',
565
                    Exception\Conflict::NODE_WITH_SAME_NAME_ALREADY_EXISTS
566
                );
567
            }
568
            if (NodeInterface::CONFLICT_RENAME === $conflict) {
569
                $name = $this->getDuplicateName($name);
570
            }
571
        }
572
573
        if ($this->isDeleted()) {
574
            throw new Exception\Conflict(
575
                'could not add node '.$name.' into a deleted parent collection',
576
                Exception\Conflict::DELETED_PARENT
577
            );
578
        }
579
580
        try {
581
            $meta = [
582
                'name' => $name,
583
                'deleted' => false,
584
                'parent' => $this->getRealId(),
585
                'directory' => true,
586
                'created' => new UTCDateTime(),
587
                'changed' => new UTCDateTime(),
588
                'shared' => (true === $this->shared ? $this->getRealId() : $this->shared),
589
            ];
590
591
            if (null !== $this->_user) {
592
                $meta['owner'] = $this->_user->getId();
593
            }
594
595
            $save = array_merge($meta, $attributes);
596
597
            if (isset($save['acl'])) {
598
                $this->validateAcl($save['acl']);
599
            }
600
601
            $result = $this->_db->storage->insertOne($save, [
602
                '$isolated' => true,
603
            ]);
604
605
            $save['_id'] = $result->getInsertedId();
606
607
            $this->_logger->info('added new collection ['.$save['_id'].'] under parent ['.$this->_id.']', [
608
                'category' => get_class($this),
609
            ]);
610
611
            $this->changed = $save['changed'];
612
            $this->save('changed');
613
614
            $new = $this->_fs->initNode($save);
615
            $this->_hook->run('postCreateCollection', [$this, $new, $clone]);
616
617
            return $new;
618
        } catch (\Exception $e) {
619
            $this->_logger->error('failed create new collection under parent ['.$this->_id.']', [
620
                'category' => get_class($this),
621
                'exception' => $e,
622
            ]);
623
624
            throw $e;
625
        }
626
    }
627
628
    /**
629
     * Create new file as a child from this collection.
630
     */
631
    public function addFile($name, ?ObjectId $session = null, array $attributes = [], int $conflict = NodeInterface::CONFLICT_NOACTION, bool $clone = false): File
632
    {
633
        if (!$this->_acl->isAllowed($this, 'w')) {
634
            throw new ForbiddenException(
635
                'not allowed to create new node here',
636
                ForbiddenException::NOT_ALLOWED_TO_CREATE
637
            );
638
        }
639
640
        $this->_hook->run('preCreateFile', [$this, &$name, &$attributes, &$clone]);
641
642
        if ($this->readonly) {
643
            throw new Exception\Conflict(
644
                'node is set as readonly, it is not possible to add new sub nodes',
645
                Exception\Conflict::READONLY
646
            );
647
        }
648
649
        $name = $this->checkName($name);
650
651
        if ($this->childExists($name)) {
652
            if (NodeInterface::CONFLICT_NOACTION === $conflict) {
653
                throw new Exception\Conflict(
654
                    'a node called '.$name.' does already exists in this collection',
655
                    Exception\Conflict::NODE_WITH_SAME_NAME_ALREADY_EXISTS
656
                );
657
            }
658
            if (NodeInterface::CONFLICT_RENAME === $conflict) {
659
                $name = $this->getDuplicateName($name, File::class);
660
            }
661
        }
662
663
        if ($this->isDeleted()) {
664
            throw new Exception\Conflict(
665
                'could not add node '.$name.' into a deleted parent collection',
666
                Exception\Conflict::DELETED_PARENT
667
            );
668
        }
669
670
        try {
671
            $meta = [
672
                'name' => $name,
673
                'deleted' => false,
674
                'parent' => $this->getRealId(),
675
                'directory' => false,
676
                'hash' => null,
677
                'created' => new UTCDateTime(),
678
                'changed' => new UTCDateTime(),
679
                'version' => 0,
680
                'shared' => (true === $this->shared ? $this->getRealId() : $this->shared),
681
                'storage_adapter' => $this->storage_adapter,
682
            ];
683
684
            if (null !== $this->_user) {
685
                $meta['owner'] = $this->_user->getId();
686
            }
687
688
            $save = array_merge($meta, $attributes);
689
690
            if (isset($save['acl'])) {
691
                $this->validateAcl($save['acl']);
692
            }
693
694
            $result = $this->_db->storage->insertOne($save, [
695
                '$isolated' => true,
696
            ]);
697
698
            $save['_id'] = $result->getInsertedId();
699
700
            $this->_logger->info('added new file ['.$save['_id'].'] under parent ['.$this->_id.']', [
701
                'category' => get_class($this),
702
            ]);
703
704
            $this->changed = $save['changed'];
705
            $this->save('changed');
706
707
            $file = $this->_fs->initNode($save);
708
            $file->setContent($session, $attributes);
709
            $this->_hook->run('postCreateFile', [$this, $file, $clone]);
710
711
            return $file;
712
        } catch (\Exception $e) {
713
            $this->_logger->error('failed add new file under parent ['.$this->_id.']', [
714
                'category' => get_class($this),
715
                'exception' => $e,
716
            ]);
717
718
            throw $e;
719
        }
720
    }
721
722
    /**
723
     * Create new file wrapper
724
     * (Sabe\DAV compatible method, elsewhere use addFile().
725
     *
726
     * Sabre\DAV requires that createFile() returns the ETag instead the newly created file instance
727
     *
728
     * @param string $name
729
     * @param string $data
730
     */
731
    public function createFile($name, $data = null, array $attributes = []): string
732
    {
733
        $session = $this->_storage->storeTemporaryFile($data, $this->_user);
734
        $file = $this->addFile($name, $session, $attributes);
735
736
        return $file->getETag();
737
    }
738
739
    /**
740
     * Create new directory wrapper
741
     * (Sabe\DAV compatible method, elsewhere use addDirectory().
742
     *
743
     * Sabre\DAV requires that createDirectory() returns void
744
     *
745
     * @param string $name
746
     */
747
    public function createDirectory($name): void
748
    {
749
        $this->addDirectory($name);
750
    }
751
752
    /**
753
     * Do recursive Action.
754
     */
755
    public function doRecursiveAction(callable $callable, int $deleted = NodeInterface::DELETED_EXCLUDE): bool
756
    {
757
        $children = $this->getChildNodes($deleted, []);
758
759
        foreach ($children as $child) {
760
            $callable($child);
761
        }
762
763
        return true;
764
    }
765
766
    /**
767
     * Validate acl.
768
     */
769
    protected function validateAcl(array $acl): bool
770
    {
771
        if (!$this->_acl->isAllowed($this, 'm')) {
772
            throw new ForbiddenException(
773
                 'not allowed to set acl',
774
                  ForbiddenException::NOT_ALLOWED_TO_MANAGE
775
            );
776
        }
777
778
        if (!$this->isSpecial()) {
779
            throw new Exception\Conflict('node acl may only be set on share member nodes', Exception\Conflict::NOT_SHARED);
780
        }
781
782
        $this->_acl->validateAcl($this->_server, $acl);
783
784
        return true;
785
    }
786
787
    /**
788
     * Get children query filter.
789
     *
790
     * Deleted:
791
     *  0 - Exclude deleted
792
     *  1 - Only deleted
793
     *  2 - Include deleted
794
     */
795
    protected function getChildrenFilter(int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): array
796
    {
797
        $search = [
798
            'parent' => $this->getRealId(),
799
        ];
800
801
        if (NodeInterface::DELETED_EXCLUDE === $deleted) {
802
            $search['deleted'] = false;
803
        } elseif (NodeInterface::DELETED_ONLY === $deleted) {
804
            $search['deleted'] = ['$type' => 9];
805
        }
806
807
        $search = array_merge($filter, $search);
808
809
        if ($this->shared) {
810
            $search = [
811
                '$and' => [
812
                    $search,
813
                    [
814
                        '$or' => [
815
                            ['shared' => $this->reference],
816
                            ['shared' => $this->shared],
817
                            ['shared' => $this->_id],
818
                        ],
819
                    ],
820
                ],
821
            ];
822
        } elseif (null !== $this->_user) {
823
            $search['owner'] = $this->_user->getId();
824
        }
825
826
        if ($this->filter !== null && $this->_user !== null) {
827
            $include = isset($search['deleted']) ? ['deleted' => $search['deleted']] : [];
828
            $stored_filter = ['$and' => [
829
                array_merge(
830
                    $include,
831
                    json_decode($this->filter, true),
832
                    $filter
833
                ),
834
                ['$or' => [
835
                    ['owner' => $this->_user->getId()],
836
                    ['shared' => ['$in' => $this->_user->getShares()]],
837
                ]],
838
            ]];
839
840
            $search = ['$or' => [
841
                $search,
842
                $stored_filter,
843
            ]];
844
        }
845
846
        return $search;
847
    }
848
849
    /**
850
     * Completely remove node.
851
     *
852
     * @param string $recursion Identifier to identify a recursive action
853
     */
854
    protected function _forceDelete(?string $recursion = null, bool $recursion_first = true): bool
855
    {
856
        if (!$this->isReference()) {
857
            $this->doRecursiveAction(function ($node) use ($recursion) {
858
                $node->delete(true, $recursion, false);
859
            }, NodeInterface::DELETED_INCLUDE);
860
        }
861
862
        try {
863
            $result = $this->_db->storage->deleteOne(['_id' => $this->_id]);
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...
864
865
            if ($this->isShared()) {
866
                $result = $this->_db->storage->deleteMany(['reference' => $this->_id]);
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...
867
            }
868
869
            $this->_logger->info('force removed collection ['.$this->_id.']', [
870
                'category' => get_class($this),
871
            ]);
872
873
            $this->_hook->run(
874
                'postDeleteCollection',
875
                [$this, true, $recursion, $recursion_first]
876
            );
877
        } catch (\Exception $e) {
878
            $this->_logger->error('failed force remove collection ['.$this->_id.']', [
879
                'category' => get_class($this),
880
                'exception' => $e,
881
            ]);
882
883
            throw $e;
884
        }
885
886
        return true;
887
    }
888
}
889