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\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\Adapter\AdapterInterface as StorageAdapterInterface; |
||
19 | use Balloon\Hook; |
||
20 | use Balloon\Server\User; |
||
21 | use Generator; |
||
22 | use MimeType\MimeType; |
||
23 | use function MongoDB\BSON\fromJSON; |
||
24 | use MongoDB\BSON\ObjectId; |
||
25 | use MongoDB\BSON\Regex; |
||
26 | use function MongoDB\BSON\toPHP; |
||
27 | use MongoDB\BSON\UTCDateTime; |
||
28 | use Psr\Log\LoggerInterface; |
||
29 | use Sabre\DAV\IQuota; |
||
30 | |||
31 | class Collection extends AbstractNode implements IQuota |
||
32 | { |
||
33 | /** |
||
34 | * Root folder. |
||
35 | */ |
||
36 | const ROOT_FOLDER = '/'; |
||
37 | |||
38 | /** |
||
39 | * Share acl. |
||
40 | * |
||
41 | * @var array |
||
42 | */ |
||
43 | protected $acl = []; |
||
44 | |||
45 | /** |
||
46 | * Share name. |
||
47 | * |
||
48 | * @var string |
||
49 | */ |
||
50 | protected $share_name; |
||
51 | |||
52 | /** |
||
53 | * filter. |
||
54 | * |
||
55 | * @var string |
||
56 | */ |
||
57 | protected $filter; |
||
58 | |||
59 | /** |
||
60 | * Storage. |
||
61 | * |
||
62 | * @var StorageAdapterInterface |
||
63 | */ |
||
64 | protected $_storage; |
||
65 | |||
66 | /** |
||
67 | * Initialize. |
||
68 | */ |
||
69 | public function __construct(array $attributes, Filesystem $fs, LoggerInterface $logger, Hook $hook, Acl $acl, ?Collection $parent, StorageAdapterInterface $storage) |
||
70 | { |
||
71 | $this->_fs = $fs; |
||
72 | $this->_server = $fs->getServer(); |
||
73 | $this->_db = $fs->getDatabase(); |
||
74 | $this->_user = $fs->getUser(); |
||
75 | $this->_logger = $logger; |
||
76 | $this->_hook = $hook; |
||
77 | $this->_acl = $acl; |
||
78 | $this->_storage = $storage; |
||
79 | $this->_parent = $parent; |
||
80 | |||
81 | foreach ($attributes as $attr => $value) { |
||
82 | $this->{$attr} = $value; |
||
83 | } |
||
84 | |||
85 | $this->mime = 'inode/directory'; |
||
86 | $this->raw_attributes = $attributes; |
||
87 | } |
||
88 | |||
89 | /** |
||
90 | * Set storage adapter. |
||
91 | */ |
||
92 | public function setStorage(StorageAdapterInterface $adapter): self |
||
93 | { |
||
94 | $this->_storage = $adapter; |
||
95 | |||
96 | return $this; |
||
97 | } |
||
98 | |||
99 | /** |
||
100 | * Get storage adapter. |
||
101 | */ |
||
102 | public function getStorage(): StorageAdapterInterface |
||
103 | { |
||
104 | return $this->_storage; |
||
105 | } |
||
106 | |||
107 | /** |
||
108 | * Copy node with children. |
||
109 | */ |
||
110 | public function copyTo(self $parent, int $conflict = NodeInterface::CONFLICT_NOACTION, ?string $recursion = null, bool $recursion_first = true, int $deleted = NodeInterface::DELETED_EXCLUDE): NodeInterface |
||
111 | { |
||
112 | if (null === $recursion) { |
||
113 | $recursion_first = true; |
||
114 | $recursion = uniqid(); |
||
115 | } else { |
||
116 | $recursion_first = false; |
||
117 | } |
||
118 | |||
119 | $this->_hook->run( |
||
120 | 'preCopyCollection', |
||
121 | [$this, $parent, &$conflict, &$recursion, &$recursion_first] |
||
122 | ); |
||
123 | |||
124 | if (NodeInterface::CONFLICT_RENAME === $conflict && $parent->childExists($this->name)) { |
||
125 | $name = $this->getDuplicateName(); |
||
126 | } else { |
||
127 | $name = $this->name; |
||
128 | } |
||
129 | |||
130 | if ($this->_id === $parent->getId()) { |
||
131 | throw new Exception\Conflict( |
||
132 | 'can not copy node into itself', |
||
133 | Exception\Conflict::CANT_COPY_INTO_ITSELF |
||
134 | ); |
||
135 | } |
||
136 | |||
137 | if (NodeInterface::CONFLICT_MERGE === $conflict && $parent->childExists($this->name)) { |
||
138 | $new_parent = $parent->getChild($this->name); |
||
139 | |||
140 | if ($new_parent instanceof File) { |
||
141 | $new_parent = $this; |
||
142 | } |
||
143 | } else { |
||
144 | $new_parent = $parent->addDirectory($name, [ |
||
145 | 'created' => $this->created, |
||
146 | 'changed' => $this->changed, |
||
147 | 'filter' => $this->filter, |
||
148 | 'meta' => $this->meta, |
||
149 | ], NodeInterface::CONFLICT_NOACTION, true); |
||
150 | } |
||
151 | |||
152 | foreach ($this->getChildNodes($deleted) as $child) { |
||
153 | $child->copyTo($new_parent, $conflict, $recursion, false, $deleted); |
||
154 | } |
||
155 | |||
156 | $this->_hook->run( |
||
157 | 'postCopyCollection', |
||
158 | [$this, $parent, $new_parent, $conflict, $recursion, $recursion_first] |
||
159 | ); |
||
160 | |||
161 | return $new_parent; |
||
162 | } |
||
163 | |||
164 | /** |
||
165 | * Is mount. |
||
166 | */ |
||
167 | public function isMounted(): bool |
||
168 | { |
||
169 | return count($this->mount) > 0; |
||
170 | } |
||
171 | |||
172 | /** |
||
173 | * Get Share name. |
||
174 | */ |
||
175 | public function getShareName(): string |
||
176 | { |
||
177 | if ($this->isShare()) { |
||
178 | return $this->share_name; |
||
179 | } |
||
180 | |||
181 | return $this->_fs->findRawNode($this->getShareId())['share_name']; |
||
0 ignored issues
–
show
|
|||
182 | } |
||
183 | |||
184 | /** |
||
185 | * Get Attributes. |
||
186 | */ |
||
187 | public function getAttributes(): array |
||
188 | { |
||
189 | return [ |
||
190 | '_id' => $this->_id, |
||
191 | 'name' => $this->name, |
||
192 | 'shared' => $this->shared, |
||
193 | 'share_name' => $this->share_name, |
||
194 | 'acl' => $this->acl, |
||
195 | 'directory' => true, |
||
196 | 'reference' => $this->reference, |
||
197 | 'parent' => $this->parent, |
||
198 | 'app' => $this->app, |
||
199 | 'owner' => $this->owner, |
||
200 | 'meta' => $this->meta, |
||
201 | 'mime' => $this->mime, |
||
202 | 'filter' => $this->filter, |
||
203 | 'deleted' => $this->deleted, |
||
204 | 'changed' => $this->changed, |
||
205 | 'created' => $this->created, |
||
206 | 'destroy' => $this->destroy, |
||
207 | 'readonly' => $this->readonly, |
||
208 | 'mount' => $this->mount, |
||
209 | 'storage_reference' => $this->storage_reference, |
||
210 | 'storage' => $this->storage, |
||
211 | ]; |
||
212 | } |
||
213 | |||
214 | /** |
||
215 | * Set collection filter. |
||
216 | */ |
||
217 | public function setFilter(?array $filter = null): bool |
||
218 | { |
||
219 | $this->filter = json_encode($filter); |
||
220 | |||
221 | return $this->save('filter'); |
||
222 | } |
||
223 | |||
224 | /** |
||
225 | * Get collection. |
||
226 | */ |
||
227 | public function get(): void |
||
228 | { |
||
229 | $this->getZip(); |
||
230 | } |
||
231 | |||
232 | /** |
||
233 | * Fetch children items of this collection. |
||
234 | * |
||
235 | * Deleted: |
||
236 | * 0 - Exclude deleted |
||
237 | * 1 - Only deleted |
||
238 | * 2 - Include deleted |
||
239 | * |
||
240 | * @param int $offset |
||
241 | * @param int $limit |
||
242 | */ |
||
243 | public function getChildNodes(int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = [], ?int $offset = null, ?int $limit = null): Generator |
||
244 | { |
||
245 | $filter = $this->getChildrenFilter($deleted, $filter); |
||
246 | |||
247 | return $this->_fs->findNodesByFilter($filter, $offset, $limit); |
||
248 | } |
||
249 | |||
250 | /** |
||
251 | * Fetch children items of this collection (as array). |
||
252 | * |
||
253 | * Deleted: |
||
254 | * 0 - Exclude deleted |
||
255 | * 1 - Only deleted |
||
256 | * 2 - Include deleted |
||
257 | */ |
||
258 | public function getChildren(int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): array |
||
259 | { |
||
260 | return iterator_to_array($this->getChildNodes($deleted, $filter)); |
||
261 | } |
||
262 | |||
263 | /** |
||
264 | * Is custom filter node. |
||
265 | */ |
||
266 | public function isFiltered(): bool |
||
267 | { |
||
268 | return !empty($this->filter); |
||
269 | } |
||
270 | |||
271 | /** |
||
272 | * Get number of children. |
||
273 | */ |
||
274 | public function getSize(): int |
||
275 | { |
||
276 | return count($this->getChildren()); |
||
277 | } |
||
278 | |||
279 | /** |
||
280 | * Get real id (reference). |
||
281 | * |
||
282 | * @return ObjectId |
||
283 | */ |
||
284 | public function getRealId(): ?ObjectId |
||
285 | { |
||
286 | if (true === $this->shared && $this->isReference()) { |
||
287 | return $this->reference; |
||
288 | } |
||
289 | |||
290 | return $this->_id; |
||
291 | } |
||
292 | |||
293 | /** |
||
294 | * Get user quota information. |
||
295 | */ |
||
296 | public function getQuotaInfo(): array |
||
297 | { |
||
298 | $quota = $this->_user->getQuotaUsage(); |
||
299 | |||
300 | return [ |
||
301 | $quota['used'], |
||
302 | $quota['available'], |
||
303 | ]; |
||
304 | } |
||
305 | |||
306 | /** |
||
307 | * Fetch children items of this collection. |
||
308 | */ |
||
309 | public function getChild($name, int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): NodeInterface |
||
310 | { |
||
311 | $name = $this->checkName($name); |
||
312 | $filter = $this->getChildrenFilter($deleted, $filter); |
||
313 | $filter['name'] = new Regex('^'.preg_quote($name).'$', 'i'); |
||
314 | $node = $this->_db->storage->findOne($filter); |
||
315 | |||
316 | if (null === $node) { |
||
317 | throw new Exception\NotFound( |
||
318 | 'node called '.$name.' does not exists here', |
||
319 | Exception\NotFound::NODE_NOT_FOUND |
||
320 | ); |
||
321 | } |
||
322 | |||
323 | $this->_logger->debug('loaded node ['.$node['_id'].' from parent node ['.$this->getRealId().']', [ |
||
324 | 'category' => get_class($this), |
||
325 | ]); |
||
326 | |||
327 | return $this->_fs->initNode($node); |
||
328 | } |
||
329 | |||
330 | /** |
||
331 | * Delete node. |
||
332 | * |
||
333 | * Actually the node will not be deleted (Just set a delete flag), set $force=true to |
||
334 | * delete finally |
||
335 | */ |
||
336 | public function delete(bool $force = false, ?string $recursion = null, bool $recursion_first = true): bool |
||
337 | { |
||
338 | if (!$this->isReference() && !$this->_acl->isAllowed($this, 'w')) { |
||
339 | throw new ForbiddenException( |
||
340 | 'not allowed to delete node '.$this->name, |
||
341 | ForbiddenException::NOT_ALLOWED_TO_DELETE |
||
342 | ); |
||
343 | } |
||
344 | |||
345 | if (null === $recursion) { |
||
346 | $recursion_first = true; |
||
347 | $recursion = uniqid(); |
||
348 | } else { |
||
349 | $recursion_first = false; |
||
350 | } |
||
351 | |||
352 | $this->_hook->run( |
||
353 | 'preDeleteCollection', |
||
354 | [$this, &$force, &$recursion, &$recursion_first] |
||
355 | ); |
||
356 | |||
357 | if (true === $force) { |
||
358 | return $this->_forceDelete($recursion, $recursion_first); |
||
359 | } |
||
360 | |||
361 | $this->deleted = new UTCDateTime(); |
||
362 | $this->storage = $this->_parent->getStorage()->deleteCollection($this); |
||
363 | |||
364 | if (!$this->isReference() && !$this->isMounted() && !$this->isFiltered()) { |
||
365 | $this->doRecursiveAction(function ($node) use ($recursion) { |
||
366 | $node->delete(false, $recursion, false); |
||
367 | }, NodeInterface::DELETED_EXCLUDE); |
||
368 | } |
||
369 | |||
370 | if (null !== $this->_id) { |
||
371 | $result = $this->save([ |
||
372 | 'deleted', 'storage', |
||
373 | ], [], $recursion, false); |
||
374 | } else { |
||
375 | $result = true; |
||
376 | } |
||
377 | |||
378 | $this->_hook->run( |
||
379 | 'postDeleteCollection', |
||
380 | [$this, $force, $recursion, $recursion_first] |
||
381 | ); |
||
382 | |||
383 | return $result; |
||
384 | } |
||
385 | |||
386 | /** |
||
387 | * Check if this collection has child named $name. |
||
388 | * |
||
389 | * deleted: |
||
390 | * |
||
391 | * 0 - Exclude deleted |
||
392 | * 1 - Only deleted |
||
393 | * 2 - Include deleted |
||
394 | * |
||
395 | * @param string $name |
||
396 | * @param int $deleted |
||
397 | */ |
||
398 | public function childExists($name, $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): bool |
||
399 | { |
||
400 | $name = $this->checkName($name); |
||
401 | |||
402 | $find = [ |
||
403 | 'parent' => $this->getRealId(), |
||
404 | 'name' => new Regex('^'.preg_quote($name).'$', 'i'), |
||
405 | ]; |
||
406 | |||
407 | if (null !== $this->_user) { |
||
408 | $find['owner'] = $this->_user->getId(); |
||
409 | } |
||
410 | |||
411 | switch ($deleted) { |
||
412 | case NodeInterface::DELETED_EXCLUDE: |
||
413 | $find['deleted'] = false; |
||
414 | |||
415 | break; |
||
416 | case NodeInterface::DELETED_ONLY: |
||
417 | $find['deleted'] = ['$type' => 9]; |
||
418 | |||
419 | break; |
||
420 | } |
||
421 | |||
422 | $find = array_merge($filter, $find); |
||
423 | |||
424 | if ($this->isSpecial()) { |
||
425 | unset($find['owner']); |
||
426 | } |
||
427 | |||
428 | $node = $this->_db->storage->findOne($find); |
||
429 | |||
430 | return (bool) $node; |
||
431 | } |
||
432 | |||
433 | /** |
||
434 | * Share collection. |
||
435 | */ |
||
436 | public function share(array $acl, string $name): bool |
||
437 | { |
||
438 | if ($this->isShareMember()) { |
||
439 | throw new Exception('a sub node of a share can not be shared'); |
||
440 | } |
||
441 | |||
442 | $this->_acl->validateAcl($this->_server, $acl); |
||
443 | |||
444 | $action = [ |
||
445 | '$set' => [ |
||
446 | 'shared' => $this->getRealId(), |
||
447 | ], |
||
448 | ]; |
||
449 | |||
450 | $toset = $this->getChildrenRecursive($this->getRealId(), $shares); |
||
451 | |||
452 | if (!empty($shares)) { |
||
453 | throw new Exception('child folder contains a shared folder'); |
||
454 | } |
||
455 | |||
456 | $this->_db->storage->updateMany([ |
||
457 | '_id' => [ |
||
458 | '$in' => $toset, |
||
459 | ], |
||
460 | ], $action); |
||
461 | |||
462 | $this->_db->delta->updateMany([ |
||
463 | '_id' => [ |
||
464 | '$in' => $toset, |
||
465 | ], |
||
466 | ], $action); |
||
467 | |||
468 | if ($this->getRealId() === $this->_id) { |
||
469 | $this->acl = $acl; |
||
470 | $this->shared = true; |
||
471 | $this->share_name = $name; |
||
472 | $this->save(['acl', 'shared', 'share_name']); |
||
473 | } else { |
||
474 | $this->_db->storage->updateOne([ |
||
475 | '_id' => $this->getRealId(), |
||
476 | ], [ |
||
477 | '$set' => [ |
||
478 | 'share_name' => $name, |
||
479 | 'acl' => $acl, |
||
480 | ], |
||
481 | ]); |
||
482 | } |
||
483 | |||
484 | return true; |
||
485 | } |
||
486 | |||
487 | /** |
||
488 | * Unshare collection. |
||
489 | */ |
||
490 | public function unshare(): bool |
||
491 | { |
||
492 | if (!$this->_acl->isAllowed($this, 'm')) { |
||
493 | throw new ForbiddenException( |
||
494 | 'not allowed to share node', |
||
495 | ForbiddenException::NOT_ALLOWED_TO_MANAGE |
||
496 | ); |
||
497 | } |
||
498 | |||
499 | if (true !== $this->shared) { |
||
500 | throw new Exception\Conflict( |
||
501 | 'Can not unshare a none shared collection', |
||
502 | Exception\Conflict::NOT_SHARED |
||
503 | ); |
||
504 | } |
||
505 | |||
506 | $this->shared = false; |
||
507 | $this->share_name = null; |
||
508 | $this->acl = []; |
||
509 | $action = [ |
||
510 | '$set' => [ |
||
511 | 'owner' => $this->_user->getId(), |
||
512 | 'shared' => false, |
||
513 | ], |
||
514 | ]; |
||
515 | |||
516 | $toset = $this->getChildrenRecursive($this->getRealId(), $shares); |
||
517 | |||
518 | $this->_db->storage->updateMany([ |
||
519 | '_id' => [ |
||
520 | '$in' => $toset, |
||
521 | ], |
||
522 | ], $action); |
||
523 | |||
524 | $result = $this->save(['shared'], ['acl', 'share_name']); |
||
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
Loading history...
|
|||
525 | |||
526 | return true; |
||
527 | } |
||
528 | |||
529 | /** |
||
530 | * Get children. |
||
531 | */ |
||
532 | public function getChildrenRecursive(?ObjectId $id = null, ?array &$shares = []): array |
||
533 | { |
||
534 | $list = []; |
||
535 | $result = $this->_db->storage->find([ |
||
536 | 'parent' => $id, |
||
537 | ], [ |
||
538 | '_id' => 1, |
||
539 | 'directory' => 1, |
||
540 | 'reference' => 1, |
||
541 | 'shared' => 1, |
||
542 | ]); |
||
543 | |||
544 | foreach ($result as $node) { |
||
545 | $list[] = $node['_id']; |
||
546 | |||
547 | if ($node['directory'] === true) { |
||
548 | if (isset($node['reference']) || isset($node['shared']) && true === $node['shared']) { |
||
549 | $shares[] = $node['_id']; |
||
550 | } |
||
551 | |||
552 | if (true === $node['directory'] && !isset($node['reference'])) { |
||
553 | $list = array_merge($list, $this->getChildrenRecursive($node['_id'], $shares)); |
||
554 | } |
||
555 | } |
||
556 | } |
||
557 | |||
558 | return $list; |
||
559 | } |
||
560 | |||
561 | /** |
||
562 | * Create new directory. |
||
563 | */ |
||
564 | public function addDirectory($name, array $attributes = [], int $conflict = NodeInterface::CONFLICT_NOACTION, bool $clone = false): self |
||
565 | { |
||
566 | if (!$this->_acl->isAllowed($this, 'w')) { |
||
567 | throw new ForbiddenException( |
||
568 | 'not allowed to create new node here', |
||
569 | ForbiddenException::NOT_ALLOWED_TO_CREATE |
||
570 | ); |
||
571 | } |
||
572 | |||
573 | $this->_hook->run('preCreateCollection', [$this, &$name, &$attributes, &$clone]); |
||
574 | |||
575 | if ($this->readonly) { |
||
576 | throw new Exception\Conflict( |
||
577 | 'node is set as readonly, it is not possible to add new sub nodes', |
||
578 | Exception\Conflict::READONLY |
||
579 | ); |
||
580 | } |
||
581 | |||
582 | $name = $this->checkName($name); |
||
583 | |||
584 | if ($this->childExists($name)) { |
||
585 | if (NodeInterface::CONFLICT_NOACTION === $conflict) { |
||
586 | throw new Exception\Conflict( |
||
587 | 'a node called '.$name.' does already exists in this collection', |
||
588 | Exception\Conflict::NODE_WITH_SAME_NAME_ALREADY_EXISTS |
||
589 | ); |
||
590 | } |
||
591 | if (NodeInterface::CONFLICT_RENAME === $conflict) { |
||
592 | $name = $this->getDuplicateName($name); |
||
593 | } |
||
594 | } |
||
595 | |||
596 | if ($this->isDeleted()) { |
||
597 | throw new Exception\Conflict( |
||
598 | 'could not add node '.$name.' into a deleted parent collection', |
||
599 | Exception\Conflict::DELETED_PARENT |
||
600 | ); |
||
601 | } |
||
602 | |||
603 | $id = new ObjectId(); |
||
604 | |||
605 | try { |
||
606 | $meta = [ |
||
607 | '_id' => $id, |
||
608 | 'pointer' => $id, |
||
609 | 'name' => $name, |
||
610 | 'deleted' => false, |
||
611 | 'parent' => $this->getRealId(), |
||
612 | 'directory' => true, |
||
613 | 'created' => new UTCDateTime(), |
||
614 | 'changed' => new UTCDateTime(), |
||
615 | 'shared' => (true === $this->shared ? $this->getRealId() : $this->shared), |
||
616 | 'storage' => $this->_storage->createCollection($this, $name), |
||
617 | 'storage_reference' => $this->getMount(), |
||
618 | ]; |
||
619 | |||
620 | if (null !== $this->_user) { |
||
621 | $meta['owner'] = $this->_user->getId(); |
||
622 | } |
||
623 | |||
624 | $save = array_merge($meta, $attributes); |
||
625 | |||
626 | if (isset($save['filter'])) { |
||
627 | $this->validateFilter($save['filter']); |
||
628 | } |
||
629 | |||
630 | if (isset($save['acl'])) { |
||
631 | $this->validateAcl($save['acl']); |
||
632 | } |
||
633 | |||
634 | $result = $this->_db->storage->insertOne($save); |
||
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
Loading history...
|
|||
635 | |||
636 | $this->_logger->info('added new collection ['.$save['_id'].'] under parent ['.$this->_id.']', [ |
||
637 | 'category' => get_class($this), |
||
638 | ]); |
||
639 | |||
640 | $this->changed = $save['changed']; |
||
641 | $this->save('changed'); |
||
642 | |||
643 | $new = $this->_fs->initNode($save); |
||
644 | $this->_hook->run('postCreateCollection', [$this, $new, $clone]); |
||
645 | |||
646 | return $new; |
||
647 | } catch (\Exception $e) { |
||
648 | $this->_logger->error('failed create new collection under parent ['.$this->_id.']', [ |
||
649 | 'category' => get_class($this), |
||
650 | 'exception' => $e, |
||
651 | ]); |
||
652 | |||
653 | throw $e; |
||
654 | } |
||
655 | } |
||
656 | |||
657 | /** |
||
658 | * Create new file as a child from this collection. |
||
659 | */ |
||
660 | public function addFile($name, ?ObjectId $session = null, array $attributes = [], int $conflict = NodeInterface::CONFLICT_NOACTION, bool $clone = false): File |
||
661 | { |
||
662 | if (!$this->_acl->isAllowed($this, 'w')) { |
||
663 | throw new ForbiddenException( |
||
664 | 'not allowed to create new node here', |
||
665 | ForbiddenException::NOT_ALLOWED_TO_CREATE |
||
666 | ); |
||
667 | } |
||
668 | |||
669 | $this->_hook->run('preCreateFile', [$this, &$name, &$attributes, &$clone]); |
||
670 | |||
671 | if ($this->readonly) { |
||
672 | throw new Exception\Conflict( |
||
673 | 'node is set as readonly, it is not possible to add new sub nodes', |
||
674 | Exception\Conflict::READONLY |
||
675 | ); |
||
676 | } |
||
677 | |||
678 | $name = $this->checkName($name); |
||
679 | |||
680 | if ($this->childExists($name)) { |
||
681 | if (NodeInterface::CONFLICT_NOACTION === $conflict) { |
||
682 | throw new Exception\Conflict( |
||
683 | 'a node called '.$name.' does already exists in this collection', |
||
684 | Exception\Conflict::NODE_WITH_SAME_NAME_ALREADY_EXISTS |
||
685 | ); |
||
686 | } |
||
687 | if (NodeInterface::CONFLICT_RENAME === $conflict) { |
||
688 | $name = $this->getDuplicateName($name, File::class); |
||
689 | } |
||
690 | } |
||
691 | |||
692 | if ($this->isDeleted()) { |
||
693 | throw new Exception\Conflict( |
||
694 | 'could not add node '.$name.' into a deleted parent collection', |
||
695 | Exception\Conflict::DELETED_PARENT |
||
696 | ); |
||
697 | } |
||
698 | |||
699 | $id = new ObjectId(); |
||
700 | |||
701 | try { |
||
702 | $meta = [ |
||
703 | '_id' => $id, |
||
704 | 'pointer' => $id, |
||
705 | 'name' => $name, |
||
706 | 'deleted' => false, |
||
707 | 'parent' => $this->getRealId(), |
||
708 | 'directory' => false, |
||
709 | 'hash' => null, |
||
710 | 'mime' => MimeType::getType($name), |
||
711 | 'created' => new UTCDateTime(), |
||
712 | 'changed' => new UTCDateTime(), |
||
713 | 'version' => 0, |
||
714 | 'shared' => (true === $this->shared ? $this->getRealId() : $this->shared), |
||
715 | 'storage_reference' => $this->getMount(), |
||
716 | ]; |
||
717 | |||
718 | if (null !== $this->_user) { |
||
719 | $meta['owner'] = $this->_user->getId(); |
||
720 | } |
||
721 | |||
722 | $save = array_merge($meta, $attributes); |
||
723 | |||
724 | if (isset($save['acl'])) { |
||
725 | $this->validateAcl($save['acl']); |
||
726 | } |
||
727 | |||
728 | $result = $this->_db->storage->insertOne($save); |
||
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
Loading history...
|
|||
729 | |||
730 | $this->_logger->info('added new file ['.$save['_id'].'] under parent ['.$this->_id.']', [ |
||
731 | 'category' => get_class($this), |
||
732 | ]); |
||
733 | |||
734 | $this->changed = $save['changed']; |
||
735 | $this->save('changed'); |
||
736 | |||
737 | $file = $this->_fs->initNode($save); |
||
738 | |||
739 | if ($session !== null) { |
||
740 | $file->setContent($session, $attributes); |
||
741 | } |
||
742 | |||
743 | $this->_hook->run('postCreateFile', [$this, $file, $clone]); |
||
744 | |||
745 | return $file; |
||
746 | } catch (\Exception $e) { |
||
747 | $this->_logger->error('failed add new file under parent ['.$this->_id.']', [ |
||
748 | 'category' => get_class($this), |
||
749 | 'exception' => $e, |
||
750 | ]); |
||
751 | |||
752 | throw $e; |
||
753 | } |
||
754 | } |
||
755 | |||
756 | /** |
||
757 | * Create new file wrapper |
||
758 | * (Sabe\DAV compatible method, elsewhere use addFile(). |
||
759 | * |
||
760 | * Sabre\DAV requires that createFile() returns the ETag instead the newly created file instance |
||
761 | * |
||
762 | * @param string $name |
||
763 | * @param string $data |
||
764 | */ |
||
765 | public function createFile($name, $data = null): string |
||
766 | { |
||
767 | $session = $this->_storage->storeTemporaryFile($data, $this->_user); |
||
768 | |||
769 | if ($this->childExists($name, NodeInterface::DELETED_INCLUDE, ['directory' => false])) { |
||
770 | $file = $this->getChild($name, NodeInterface::DELETED_INCLUDE, ['directory' => false]); |
||
771 | $file->setContent($session); |
||
772 | } else { |
||
773 | $file = $this->addFile($name, $session); |
||
774 | } |
||
775 | |||
776 | return $file->getETag(); |
||
0 ignored issues
–
show
The method
getETag does only exist in Balloon\Filesystem\Node\File , but not in Balloon\Filesystem\Node\NodeInterface .
It seems like the method you are trying to call exists only in some of the possible types. Let’s take a look at an example: class A
{
public function foo() { }
}
class B extends A
{
public function bar() { }
}
/**
* @param A|B $x
*/
function someFunction($x)
{
$x->foo(); // This call is fine as the method exists in A and B.
$x->bar(); // This method only exists in B and might cause an error.
}
Available Fixes
Loading history...
|
|||
777 | } |
||
778 | |||
779 | /** |
||
780 | * Create new directory wrapper |
||
781 | * (Sabe\DAV compatible method, elsewhere use addDirectory(). |
||
782 | * |
||
783 | * Sabre\DAV requires that createDirectory() returns void |
||
784 | * |
||
785 | * @param string $name |
||
786 | */ |
||
787 | public function createDirectory($name): void |
||
788 | { |
||
789 | $this->addDirectory($name); |
||
790 | } |
||
791 | |||
792 | /** |
||
793 | * Do recursive Action. |
||
794 | */ |
||
795 | public function doRecursiveAction(callable $callable, int $deleted = NodeInterface::DELETED_EXCLUDE): bool |
||
796 | { |
||
797 | $children = $this->getChildNodes($deleted, []); |
||
798 | |||
799 | foreach ($children as $child) { |
||
800 | $callable($child); |
||
801 | } |
||
802 | |||
803 | return true; |
||
804 | } |
||
805 | |||
806 | /** |
||
807 | * Validate filtered collection query. |
||
808 | */ |
||
809 | protected function validateFilter(string $filter): bool |
||
810 | { |
||
811 | $filter = toPHP(fromJSON($filter), [ |
||
812 | 'root' => 'array', |
||
813 | 'document' => 'array', |
||
814 | 'array' => 'array', |
||
815 | ]); |
||
816 | |||
817 | $this->_db->storage->findOne($filter); |
||
818 | |||
819 | return true; |
||
820 | } |
||
821 | |||
822 | /** |
||
823 | * Validate acl. |
||
824 | */ |
||
825 | protected function validateAcl(array $acl): bool |
||
826 | { |
||
827 | if (!$this->_acl->isAllowed($this, 'm')) { |
||
828 | throw new ForbiddenException( |
||
829 | 'not allowed to set acl', |
||
830 | ForbiddenException::NOT_ALLOWED_TO_MANAGE |
||
831 | ); |
||
832 | } |
||
833 | |||
834 | if (!$this->isSpecial()) { |
||
835 | throw new Exception\Conflict('node acl may only be set on share member nodes', Exception\Conflict::NOT_SHARED); |
||
836 | } |
||
837 | |||
838 | $this->_acl->validateAcl($this->_server, $acl); |
||
839 | |||
840 | return true; |
||
841 | } |
||
842 | |||
843 | /** |
||
844 | * Get children query filter. |
||
845 | * |
||
846 | * Deleted: |
||
847 | * 0 - Exclude deleted |
||
848 | * 1 - Only deleted |
||
849 | * 2 - Include deleted |
||
850 | */ |
||
851 | protected function getChildrenFilter(int $deleted = NodeInterface::DELETED_EXCLUDE, array $filter = []): array |
||
852 | { |
||
853 | $search = [ |
||
854 | 'parent' => $this->getRealId(), |
||
855 | ]; |
||
856 | |||
857 | if (NodeInterface::DELETED_EXCLUDE === $deleted) { |
||
858 | $search['deleted'] = false; |
||
859 | } elseif (NodeInterface::DELETED_ONLY === $deleted) { |
||
860 | $search['deleted'] = ['$type' => 9]; |
||
861 | } |
||
862 | |||
863 | $search = array_merge($filter, $search); |
||
864 | |||
865 | if ($this->shared) { |
||
866 | $search = [ |
||
867 | '$and' => [ |
||
868 | $search, |
||
869 | [ |
||
870 | '$or' => [ |
||
871 | ['shared' => $this->reference], |
||
872 | ['shared' => $this->shared], |
||
873 | ['shared' => $this->_id], |
||
874 | ], |
||
875 | ], |
||
876 | ], |
||
877 | ]; |
||
878 | } elseif (null !== $this->_user) { |
||
879 | $search['owner'] = $this->_user->getId(); |
||
880 | } |
||
881 | |||
882 | if ($this->filter !== null && $this->_user !== null) { |
||
883 | $stored = toPHP(fromJSON($this->filter), [ |
||
884 | 'root' => 'array', |
||
885 | 'document' => 'array', |
||
886 | 'array' => 'array', |
||
887 | ]); |
||
888 | |||
889 | $include = isset($search['deleted']) ? ['deleted' => $search['deleted']] : []; |
||
890 | $stored_filter = ['$and' => [ |
||
891 | array_merge( |
||
892 | $include, |
||
893 | $stored, |
||
894 | $filter |
||
895 | ), |
||
896 | ['$or' => [ |
||
897 | ['owner' => $this->_user->getId()], |
||
898 | ['shared' => ['$in' => $this->_user->getShares()]], |
||
899 | ]], |
||
900 | [ |
||
901 | '_id' => ['$ne' => $this->_id], |
||
902 | ], |
||
903 | ]]; |
||
904 | |||
905 | $search = ['$or' => [ |
||
906 | $search, |
||
907 | $stored_filter, |
||
908 | ]]; |
||
909 | } |
||
910 | |||
911 | return $search; |
||
912 | } |
||
913 | |||
914 | /** |
||
915 | * Completely remove node. |
||
916 | */ |
||
917 | protected function _forceDelete(?string $recursion = null, bool $recursion_first = true): bool |
||
918 | { |
||
919 | if (!$this->isReference() && !$this->isMounted() && !$this->isFiltered()) { |
||
920 | $this->doRecursiveAction(function ($node) use ($recursion) { |
||
921 | $node->delete(true, $recursion, false); |
||
922 | }, NodeInterface::DELETED_INCLUDE); |
||
923 | } |
||
924 | |||
925 | try { |
||
926 | $this->_parent->getStorage()->forceDeleteCollection($this); |
||
927 | $result = $this->_db->storage->deleteOne(['_id' => $this->_id]); |
||
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
Loading history...
|
|||
928 | |||
929 | if ($this->isShared()) { |
||
930 | $result = $this->_db->storage->deleteMany(['reference' => $this->_id]); |
||
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
Loading history...
|
|||
931 | } |
||
932 | |||
933 | $this->_logger->info('force removed collection ['.$this->_id.']', [ |
||
934 | 'category' => get_class($this), |
||
935 | ]); |
||
936 | |||
937 | $this->_hook->run( |
||
938 | 'postDeleteCollection', |
||
939 | [$this, true, $recursion, $recursion_first] |
||
940 | ); |
||
941 | } catch (\Exception $e) { |
||
942 | $this->_logger->error('failed force remove collection ['.$this->_id.']', [ |
||
943 | 'category' => get_class($this), |
||
944 | 'exception' => $e, |
||
945 | ]); |
||
946 | |||
947 | throw $e; |
||
948 | } |
||
949 | |||
950 | return true; |
||
951 | } |
||
952 | } |
||
953 |
This check looks at variables that 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.