ElfinderConnectorAction::setDataFromRequest()   A
last analyzed

Complexity

Conditions 4
Paths 8

Size

Total Lines 18
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 9
nc 8
nop 1
dl 0
loc 18
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace Charcoal\Admin\Action;
4
5
use RuntimeException;
6
7
// From PSR-7
8
use Psr\Http\Message\RequestInterface;
9
use Psr\Http\Message\ResponseInterface;
10
use Psr\Http\Message\UriInterface;
11
12
// From Pimple
13
use Pimple\Container;
14
15
// From elFinder
16
use elFinder;
17
use elFinderConnector;
18
use elFinderVolumeDriver;
19
20
// From 'charcoal-config'
21
use Charcoal\Config\ConfigInterface;
22
23
// From 'charcoal-factory'
24
use Charcoal\Factory\FactoryInterface;
25
26
// From 'charcoal-property'
27
use Charcoal\Property\PropertyInterface;
28
29
// From 'charcoal-app'
30
use Charcoal\App\CallableResolverAwareTrait;
31
32
// From 'charcoal-admin'
33
use Charcoal\Admin\AdminAction;
34
use Charcoal\Admin\Template\ElfinderTemplate;
35
36
/**
37
 * Action: Setup elFinder Connector
38
 */
39
class ElfinderConnectorAction extends AdminAction
40
{
41
    use CallableResolverAwareTrait;
42
43
    /**
44
     * The default relative path (from filesystem's root) to the storage directory.
45
     *
46
     * @const string
47
     */
48
    const DEFAULT_STORAGE_PATH = 'uploads';
49
50
    /**
51
     * The base path for the Charcoal installation.
52
     *
53
     * @var string|null
54
     */
55
    protected $basePath;
56
57
    /**
58
     * The path to the public / web directory.
59
     *
60
     * @var string|null
61
     */
62
    protected $publicPath;
63
64
    /**
65
     * Store the collection of filesystem adapters.
66
     *
67
     * @var \League\Flysystem\FilesystemInterface[]
68
     */
69
    protected $filesystems;
70
71
    /**
72
     * Store the filesystem configset.
73
     *
74
     * @var \Charcoal\App\Config\FilesystemConfig
75
     */
76
    protected $filesystemConfig;
77
78
    /**
79
     * Store the elFinder configuration from the admin / app configset.
80
     *
81
     * @var ConfigInterface
82
     */
83
    protected $elfinderConfig;
84
85
    /**
86
     * Store the compiled elFinder configuration settings.
87
     *
88
     * @var array
89
     * - {@link https://github.com/Studio-42/elFinder/wiki/Connector-configuration-options}
90
     */
91
    protected $elfinderOptions;
92
93
    /**
94
     * Store the elFinder connector instance.
95
     *
96
     * @var \elFinderConnector
97
     */
98
    protected $elfinderConnector;
99
100
    /**
101
     * Store the factory instance for the current class.
102
     *
103
     * @var FactoryInterface
104
     */
105
    private $propertyFactory;
106
107
    /**
108
     * Store the current property instance for the current class.
109
     *
110
     * @var PropertyInterface
111
     */
112
    private $formProperty;
113
114
    /**
115
     * The related object type.
116
     *
117
     * @var string
118
     */
119
    private $objType;
120
121
    /**
122
     * The related object ID.
123
     *
124
     * @var string
125
     */
126
    private $objId;
127
128
    /**
129
     * The related property identifier.
130
     *
131
     * @var string
132
     */
133
    private $propertyIdent;
134
135
    /**
136
     * Sets the action data from a PSR Request object.
137
     *
138
     * @param  RequestInterface $request A PSR-7 compatible Request instance.
139
     * @return self
140
     */
141
    protected function setDataFromRequest(RequestInterface $request)
142
    {
143
        $keys = $this->validDataFromRequest();
144
        $data = $request->getParams($keys);
0 ignored issues
show
Bug introduced by
The method getParams() does not exist on Psr\Http\Message\RequestInterface. It seems like you code against a sub-type of Psr\Http\Message\RequestInterface such as Slim\Http\Request. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

144
        /** @scrutinizer ignore-call */ 
145
        $data = $request->getParams($keys);
Loading history...
145
146
        if (isset($data['obj_type'])) {
147
            $this->objType = $data['obj_type'];
148
        }
149
150
        if (isset($data['obj_id'])) {
151
            $this->objId = $data['obj_id'];
152
        }
153
154
        if (isset($data['property'])) {
155
            $this->propertyIdent = $data['property'];
156
        }
157
158
        return $this;
159
    }
160
161
    /**
162
     * Retrieve the list of parameters to extract from the HTTP request.
163
     *
164
     * @return string[]
165
     */
166
    protected function validDataFromRequest()
167
    {
168
        return [ 'obj_type', 'obj_id', 'property' ];
169
    }
170
171
    /**
172
     * @param  RequestInterface  $request  A PSR-7 compatible Request instance.
173
     * @param  ResponseInterface $response A PSR-7 compatible Response instance.
174
     * @return ResponseInterface
175
     */
176
    public function run(RequestInterface $request, ResponseInterface $response)
177
    {
178
        unset($request);
179
180
        $this->connector = $this->setupElfinder();
0 ignored issues
show
Bug Best Practice introduced by
The property connector does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
181
182
        return $response;
183
    }
184
185
    /**
186
     * Setup the elFinder connector.
187
     *
188
     * @param  array|null $extraOptions Additional settings to pass to elFinder.
189
     * @return elFinderConnector
190
     */
191
    public function setupElfinder(array $extraOptions = [])
192
    {
193
        if (!defined('ELFINDER_IMG_PARENT_URL')) {
194
            // Ensure images injected by elFinder are relative to its assets directory
195
            define('ELFINDER_IMG_PARENT_URL', $this->baseUrl(ElfinderTemplate::ELFINDER_ASSETS_REL_PATH));
196
        }
197
198
        $options = $this->buildConnectorOptions($extraOptions);
199
200
        // Run elFinder
201
        $connector = new elFinderConnector(new elFinder($options));
202
        $connector->run();
203
204
        return $connector;
205
    }
206
207
    /**
208
     * Retrieve the elFinder Connector options.
209
     *
210
     * @return array
211
     */
212
    public function getConnectorOptions()
213
    {
214
        if ($this->elfinderOptions === null) {
215
            $this->elfinderOptions = $this->buildConnectorOptions();
216
        }
217
218
        return $this->elfinderOptions;
219
    }
220
221
    /**
222
     * Build and retrieve the elFinder Connector options.
223
     *
224
     * @link    https://github.com/Studio-42/elFinder/wiki/Connector-configuration-options
225
     *     Documentation for connector options.
226
     * @example https://gist.github.com/mcaskill/5944478b1894a5bf1349bfa699387cd4
227
     *     The Connector can be customized by defining a "elfinder" structure in
228
     *     your application's admin configuration.
229
     *
230
     * @param  array $extraOptions Additional settings to pass to elFinder.
231
     * @return array
232
     */
233
    public function buildConnectorOptions(array $extraOptions = [])
234
    {
235
        $options = [
236
            'debug' => false,
237
            'roots' => $this->getCurrentRoots()
238
        ];
239
240
        $adminOptions = $this->getAdminConnectorOptions();
241
        $adminOptions = $this->parseAdminOptionsForConnectorBuild($adminOptions);
242
        $extraOptions = $this->parseExtraOptionsForConnectorBuild($extraOptions);
243
244
        $options = $this->mergeConnectorOptions($options, $adminOptions);
245
        $options = $this->mergeConnectorOptions($options, $extraOptions);
246
247
        if (isset($options['bind'])) {
248
            $options['bind'] = $this->resolveCallbacksForBindOption($options['bind']);
249
        }
250
251
        $this->elfinderOptions = $options;
252
253
        return $options;
254
    }
255
256
    /**
257
     * Merge the elFinder Connector options.
258
     *
259
     * @param  array $options1 The settings in which data is replaced.
260
     * @param  array $options2 The settings from which data is extracted.
261
     * @return array The merged settings.
262
     */
263
    protected function mergeConnectorOptions(array $options1, array $options2)
264
    {
265
        return array_replace_recursive($options1, $options2);
266
    }
267
268
    /**
269
     * Parse the admin options for the elFinder Connector.
270
     *
271
     * @param  array $options The admin settings to parse.
272
     * @return array The parsed settings.
273
     */
274
    protected function parseAdminOptionsForConnectorBuild(array $options)
275
    {
276
        // Root settings are already merged when retrieving available roots.
277
        unset($options['roots']);
278
279
        return $options;
280
    }
281
282
    /**
283
     * Parse the extra options for the elFinder Connector.
284
     *
285
     * @param  array $options The extra settings to parse.
286
     * @return array The parsed settings.
287
     */
288
    protected function parseExtraOptionsForConnectorBuild(array $options)
289
    {
290
        // Resolve callbacks on extra options
291
        if (isset($options['roots'])) {
292
            $options['roots'] = $this->resolveCallbacksForRoots($options['roots']);
293
        }
294
295
        return $options;
296
    }
297
298
    /**
299
     * Retrieve the admin's elFinder Connector options.
300
     *
301
     * Path: `config.admin.elfinder.connector`
302
     *
303
     * @return array
304
     */
305
    public function getAdminConnectorOptions()
306
    {
307
        $config = $this->elfinderConfig('connector');
308
        if (!is_array($config)) {
309
            return [];
310
        }
311
312
        return $config;
313
    }
314
315
    /**
316
     * Retrieve the default elFinder Connector options.
317
     *
318
     * @return array
319
     */
320
    protected function getDefaultElfinderRootSettings()
321
    {
322
        return [
323
            'driver'          => 'LocalFileSystem',
324
            'i18nFolderName'  => true,
325
326
            'jpgQuality'      => 80,
327
            'tmbSize'         => 200,
328
            'tmbCrop'         => true,
329
            'tmbBgColor'      => 'transparent',
330
331
            'uploadDeny'      => $this->defaultUploadDeny(),
332
            'uploadAllow'     => $this->defaultUploadAllow(),
333
            'uploadOrder'     => [ 'deny', 'allow' ],
334
            'accessControl'   => [ $this, 'checkAccess' ],
335
            'duplicateSuffix' => '_%s_'
336
        ];
337
    }
338
339
    /**
340
     * Retrieve the default Flysystem / elFinder options.
341
     *
342
     * @return array
343
     */
344
    protected function getDefaultFlysystemRootSettings()
345
    {
346
        return [
347
            'driver'       => 'Flysystem',
348
            'rootCssClass' => 'elfinder-navbar-root-local',
349
            'filesystem'   => null,
350
            'cache'        => false,
351
            'URL'          => $this->baseUrl(self::DEFAULT_STORAGE_PATH),
352
            'path'         => self::DEFAULT_STORAGE_PATH,
353
        ];
354
    }
355
356
    /**
357
     * Retrieve the default Flysystem / elFinder options.
358
     *
359
     * @param  string $ident The disk identifier.
360
     * @return array
361
     */
362
    protected function resolveFallbackRootSettings($ident)
363
    {
364
        $fsConfig   = $this->getFilesystemConfig($ident);
365
        $uploadPath = $this->defaultUploadPath();
366
367
        if (isset($fsConfig['base_url'])) {
368
            $baseUrl = rtrim($fsConfig['base_url'], '/').'/';
369
        } else {
370
            $baseUrl = $this->baseUrl();
371
        }
372
373
        return [
374
            'URL'     => $baseUrl.'/'.$uploadPath,
375
            'path'    => $uploadPath,
376
            'tmbURL'  => $this->baseUrl($uploadPath.'/.tmb'),
377
            'tmbPath' => $uploadPath.'/.tmb'
378
        ];
379
    }
380
381
    /**
382
     * Retrieve the elFinder root options for the given file system.
383
     *
384
     * Merges `config.filesystem.connections` with
385
     * {@see self::getDefaultDiskSettings() default root settings}.
386
     *
387
     * @param  string $ident The disk identifier.
388
     * @return array|null Returns an elFinder root structure or NULL.
389
     */
390
    public function getNamedRoot($ident)
391
    {
392
        if ($this->hasFilesystem($ident) === false) {
393
            return null;
394
        }
395
396
        $filesystem = $this->getFilesystem($ident);
397
        $fsConfig   = $this->getFilesystemConfig($ident);
398
        $elfConfig  = $this->getFilesystemAdminConfig($ident);
399
400
        if (isset($elfConfig['label'])) {
401
            $label = $elfConfig['label'];
402
        } elseif (isset($fsConfig['label'])) {
403
            $label = $fsConfig['label'];
404
        } else {
405
            $label = 'filesystem.disk.'.$ident;
406
        }
407
408
        $immutableSettings = [
409
            'filesystem'  => $filesystem,
410
            'alias'       => $this->translator()->translate($label),
411
        ];
412
413
        $root = array_replace_recursive(
414
            $this->getDefaultElfinderRootSettings(),
415
            $this->getDefaultFlysystemRootSettings(),
416
            $elfConfig
417
        );
418
419
        $root = array_replace_recursive(
420
            $this->resolveFallbackRootSettings($ident),
421
            $root,
422
            $immutableSettings
423
        );
424
425
        return $this->resolveCallbacksForRoot($root);
426
    }
427
428
    /**
429
     * Retrieve only the public elFinder root volumes.
430
     *
431
     * @return array
432
     */
433
    public function getPublicRoots()
434
    {
435
        $roots = [];
436
        foreach ($this->filesystems->keys() as $ident) {
437
            if ($this->isFilesystemPublic($ident)) {
438
                $disk = $this->getNamedRoot($ident);
439
                if ($disk !== null) {
440
                    $roots[$ident] = $disk;
441
                }
442
            }
443
        }
444
445
        return $roots;
446
    }
447
448
    /**
449
     * Retrieve all elFinder root volumes.
450
     *
451
     * @return array
452
     */
453
    public function getAllRoots()
454
    {
455
        $roots = [];
456
        foreach ($this->filesystems->keys() as $ident) {
457
            $disk = $this->getNamedRoot($ident);
458
            if ($disk !== null) {
459
                $roots[$ident] = $disk;
460
            }
461
        }
462
463
        return $roots;
464
    }
465
466
    /**
467
     * Retrieve only the current context's elFinder root volumes.
468
     *
469
     * @return array Returns all public root volumes
470
     *     or a subset if the context has a related form property.
471
     */
472
    public function getCurrentRoots()
473
    {
474
        $formProperty     = $this->formProperty();
475
        $targetFilesystem = $formProperty ? $formProperty->filesystem() : null;
0 ignored issues
show
Bug introduced by
The method filesystem() does not exist on Charcoal\Property\PropertyInterface. It seems like you code against a sub-type of Charcoal\Property\PropertyInterface such as Charcoal\Property\FileProperty or Charcoal\Property\HtmlProperty. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

475
        $targetFilesystem = $formProperty ? $formProperty->/** @scrutinizer ignore-call */ filesystem() : null;
Loading history...
introduced by
$formProperty is of type Charcoal\Property\PropertyInterface, thus it always evaluated to true.
Loading history...
476
477
        if ($this->hasFilesystem($targetFilesystem)) {
478
            $disk = $this->getNamedRoot($targetFilesystem);
479
480
            $startPath = $formProperty->uploadPath();
0 ignored issues
show
Bug introduced by
The method uploadPath() does not exist on Charcoal\Property\PropertyInterface. It seems like you code against a sub-type of Charcoal\Property\PropertyInterface such as Charcoal\Property\FileProperty. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

480
            /** @scrutinizer ignore-call */ 
481
            $startPath = $formProperty->uploadPath();
Loading history...
481
            $isPublic = $formProperty->publicAccess();
0 ignored issues
show
Bug introduced by
The method publicAccess() does not exist on Charcoal\Property\PropertyInterface. It seems like you code against a sub-type of Charcoal\Property\PropertyInterface such as Charcoal\Property\FileProperty. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

481
            /** @scrutinizer ignore-call */ 
482
            $isPublic = $formProperty->publicAccess();
Loading history...
482
            $basePath = $isPublic ? $this->publicPath : $this->basePath;
483
484
            if (!file_exists($basePath.$startPath)) {
485
                mkdir($basePath.$startPath, 0755, true);
486
            }
487
488
            if ($startPath) {
489
                $disk['startPath'] = $startPath;
490
            }
491
492
            return [ $disk ];
493
        }
494
495
        return $this->getPublicRoots();
496
    }
497
498
    /**
499
     * Resolve callables in a collection of elFinder's root volumes.
500
     *
501
     * @param  array $roots One or many roots with possible unresolved callables.
502
     * @return array Returns the root(s) with resolved callables.
503
     */
504
    protected function resolveCallbacksForRoots(array $roots)
505
    {
506
        foreach ($roots as $i => $root) {
507
            $roots[$i] = $this->resolveCallbacksForRoot($root);
508
        }
509
510
        return $roots;
511
    }
512
513
    /**
514
     * Resolve callables in one elFinder root volume.
515
     *
516
     * @param  array $root A root structure with possible unresolved callables.
517
     * @return array Returns the root with resolved callables.
518
     */
519
    protected function resolveCallbacksForRoot(array $root)
520
    {
521
        if (isset($root['accessControl'])) {
522
            $callable = $root['accessControl'];
523
            if (!is_callable($callable) && is_string($callable)) {
524
                $root['accessControl'] = $this->resolveCallable($callable);
525
            }
526
        }
527
528
        return $root;
529
    }
530
531
    /**
532
     * Resolve callables in elFinder's "bind" option.
533
     *
534
     * @param  array $toResolve One or many pairs of callbacks.
535
     * @return array Returns the parsed event listeners.
536
     */
537
    protected function resolveCallbacksForBindOption(array $toResolve)
538
    {
539
        $resolved = $toResolve;
540
541
        foreach ($toResolve as $actions => $callables) {
542
            foreach ($callables as $i => $callable) {
543
                if (!is_callable($callable) && is_string($callable)) {
544
                    if (0 === strpos($callable, 'Plugin.')) {
545
                        continue;
546
                    }
547
548
                    $resolved[$actions][$i] = $this->resolveCallable($callable);
549
                }
550
            }
551
        }
552
553
        return $resolved;
554
    }
555
556
    /**
557
     * Trim a file name.
558
     *
559
     * @param  string $path     The target path.
560
     * @param  string $name     The target name.
561
     * @param  string $src      The temporary file name.
562
     * @param  object $elfinder The elFinder instance.
563
     * @param  object $volume   The current volume instance.
564
     * @return true
565
     */
566
    public function sanitizeOnUploadPreSave(&$path, &$name, $src, $elfinder, $volume)
567
    {
568
        // To please PHPCS
569
        unset($path, $src, $elfinder, $volume);
570
571
        if (isset($this->elfinderOptions['plugin']['Sanitizer'])) {
572
            $opts = $this->elfinderOptions['plugin']['Sanitizer'];
573
574
            if (isset($opts['enable']) && $opts['enable']) {
575
                $mask = (is_array($opts['replace']) ? implode($opts['replace']) : $opts['replace']);
576
                $ext = '.'.pathinfo($name, PATHINFO_EXTENSION);
577
578
                // Strip leading and trailing dashes or underscores
579
                $name = trim(str_replace($ext, '', $name), $mask);
580
581
                // Squeeze multiple delimiters and whitespace with a single separator
582
                $name = preg_replace('!['.preg_quote($mask, '!').'\.\s]{2,}!', $opts['replace'], $name);
583
584
                // Reassemble the file name
585
                $name .= $ext;
586
            }
587
        }
588
589
        return true;
590
    }
591
592
    /**
593
     * Control file access.
594
     *
595
     * This method will disable accessing files/folders starting from '.' (dot)
596
     *
597
     * @param  string                $attr    Attribute name ("read", "write", "locked", "hidden").
598
     * @param  string                $path    Absolute file path.
599
     * @param  string                $data    Value of volume option `accessControlData`.
600
     * @param  \elFinderVolumeDriver $volume  ElFinder volume driver object.
601
     * @param  boolean|null          $isDir   Whether the path is a directory
602
     *     (TRUE: directory, FALSE: file, NULL: unknown).
603
     * @param  string                $relPath File path relative to the volume root directory
604
     *     started with directory separator.
605
     * @return boolean|null TRUE to allow, FALSE to deny, NULL to let elFinder decide.
606
     **/
607
    public function checkAccess($attr, $path, $data, elFinderVolumeDriver $volume, $isDir, $relPath)
608
    {
609
        unset($data, $volume, $isDir);
610
611
        $basename = basename($path);
612
        /**
613
         * If file/folder begins with '.' (dot) but without volume root,
614
         * set to FALSE if attributes are "read" or "write",
615
         * set to TRUE if attributes are other ("locked" + "hidden"),
616
         * set to NULL to let elFinder decide itself.
617
         */
618
        return ($basename[0] === '.' && strlen($relPath) !== 1)
619
                ? !($attr === 'read' || $attr === 'write')
620
                :  null;
621
    }
622
623
    /**
624
     * Retrieve the current object type from the GET parameters.
625
     *
626
     * @return string|null
627
     */
628
    public function objType()
629
    {
630
        return $this->objType;
631
    }
632
633
    /**
634
     * Retrieve the current object ID from the GET parameters.
635
     *
636
     * @return string|null
637
     */
638
    public function objId()
639
    {
640
        return $this->objId;
641
    }
642
643
    /**
644
     * Retrieve the current object's property identifier from the GET parameters.
645
     *
646
     * @return string|null
647
     */
648
    public function propertyIdent()
649
    {
650
        return $this->propertyIdent;
651
    }
652
653
    /**
654
     * Retrieve the current property.
655
     *
656
     * @return PropertyInterface
657
     */
658
    public function formProperty()
659
    {
660
        if ($this->formProperty === null) {
661
            $this->formProperty = false;
0 ignored issues
show
Documentation Bug introduced by
It seems like false of type false is incompatible with the declared type Charcoal\Property\PropertyInterface of property $formProperty.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
662
663
            if ($this->objType() && $this->propertyIdent()) {
664
                $propertyIdent = $this->propertyIdent();
665
666
                $model = $this->modelFactory()->create($this->objType());
667
                $props = $model->metadata()->properties();
668
669
                if (isset($props[$propertyIdent])) {
670
                    $propertyMetadata = $props[$propertyIdent];
671
672
                    $property = $this->propertyFactory()->create($propertyMetadata['type']);
673
674
                    $property->setIdent($propertyIdent);
675
                    $property->setData($propertyMetadata);
676
677
                    $this->formProperty = $property;
678
                }
679
            }
680
        }
681
682
        return $this->formProperty;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->formProperty could also return false which is incompatible with the documented return type Charcoal\Property\PropertyInterface. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
683
    }
684
685
    /**
686
     * Retrieve the default root path.
687
     *
688
     * @return string
689
     */
690
    public function defaultUploadPath()
691
    {
692
        return self::DEFAULT_STORAGE_PATH;
693
    }
694
695
    /**
696
     * Allow upload for a subset MIME types.
697
     *
698
     * @return array
699
     */
700
    protected function defaultUploadAllow()
701
    {
702
        // By default, all images, PDF, and plain-text files are allowed.
703
        return [
704
            'image',
705
            'application/pdf',
706
            'text/plain'
707
        ];
708
    }
709
710
    /**
711
     * Deny upload for all MIME types.
712
     *
713
     * @return array
714
     */
715
    protected function defaultUploadDeny()
716
    {
717
        // By default, all files are rejected.
718
        return [
719
            'all'
720
        ];
721
    }
722
723
    /**
724
     * Default attributes for files and directories.
725
     *
726
     * @return array
727
     */
728
    protected function attributesForHiddenFiles()
729
    {
730
        return [
731
            // Block access to all hidden files and directories (anything starting with ".")
732
            'pattern' => '!(?:^|/)\..+$!',
733
            'read'    => false,
734
            'write'   => false,
735
            'hidden'  => true,
736
            'locked'  => false
737
        ];
738
    }
739
740
    /**
741
     * Inject dependencies from a DI Container.
742
     *
743
     * @param  Container $container A dependencies container instance.
744
     * @return void
745
     */
746
    public function setDependencies(Container $container)
747
    {
748
        parent::setDependencies($container);
749
750
        $this->basePath = $container['config']['base_path'];
751
        $this->publicPath = $container['config']['public_path'];
752
753
        $this->setElfinderConfig($container['elfinder/config']);
754
        $this->setPropertyFactory($container['property/factory']);
755
        $this->setCallableResolver($container['callableResolver']);
756
757
        /** @see \Charcoal\App\ServiceProvide\FilesystemServiceProvider */
758
        $this->filesystemConfig = $container['filesystem/config'];
759
        $this->filesystems = $container['filesystems'];
760
    }
761
762
    /**
763
     * Get the named filesystem object.
764
     *
765
     * @param  string $ident The filesystem identifier.
766
     * @return \League\Flysystem\FilesystemInterface|null Returns the filesystem instance
767
     *     or NULL if not found.
768
     */
769
    protected function getFilesystem($ident)
770
    {
771
        if (isset($this->filesystems[$ident])) {
772
            return $this->filesystems[$ident];
773
        }
774
775
        return null;
776
    }
777
778
    /**
779
     * Determine if the named filesystem object exists.
780
     *
781
     * @param  string $ident The filesystem identifier.
782
     * @return boolean TRUE if the filesystem instance exists, otherwise FALSE.
783
     */
784
    protected function hasFilesystem($ident)
785
    {
786
        return ($this->getFilesystem($ident) !== null);
787
    }
788
789
    /**
790
     * Get the given filesystem's storage configset.
791
     *
792
     * @param  string $ident The filesystem identifier.
793
     * @return array|null Returns the filesystem configset
794
     *     or NULL if the filesystem is not found.
795
     */
796
    protected function getFilesystemConfig($ident)
797
    {
798
        if ($this->hasFilesystem($ident) === false) {
799
            return null;
800
        }
801
802
        if (isset($this->filesystemConfig['connections'][$ident])) {
803
            return $this->filesystemConfig['connections'][$ident];
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->filesystem...['connections'][$ident] also could return the type Charcoal\Config\AbstractConfig|mixed which is incompatible with the documented return type array|null.
Loading history...
804
        }
805
806
        return [];
807
    }
808
809
    /**
810
     * Determine if the named filesystem is public (from its configset).
811
     *
812
     * @param  string $ident The filesystem identifier.
813
     * @return boolean TRUE if the filesystem is public, otherwise FALSE.
814
     */
815
    protected function isFilesystemPublic($ident)
816
    {
817
        if ($this->hasFilesystem($ident) === false) {
818
            return false;
819
        }
820
821
        $config = $this->getFilesystemConfig($ident);
822
        if (isset($config['public']) && $config['public'] === false) {
823
            return false;
824
        }
825
826
        return true;
827
    }
828
829
    /**
830
     * Get the given filesystem's admin configset.
831
     *
832
     * @param  string $ident The filesystem identifier.
833
     * @return array|null Returns the filesystem configset
834
     *     or NULL if the filesystem is not found.
835
     */
836
    protected function getFilesystemAdminConfig($ident)
837
    {
838
        if ($this->hasFilesystem($ident) === false) {
839
            return null;
840
        }
841
842
        $elfConfig = $this->getAdminConnectorOptions();
843
        if (isset($elfConfig['roots'][$ident])) {
844
            return $elfConfig['roots'][$ident];
845
        }
846
847
        return [];
848
    }
849
850
    /**
851
     * Set the elFinder's configset.
852
     *
853
     * @param  ConfigInterface $config A configset.
854
     * @return void
855
     */
856
    protected function setElfinderConfig(ConfigInterface $config)
857
    {
858
        $this->elfinderConfig = $config;
859
    }
860
861
    /**
862
     * Retrieve the elFinder's configset.
863
     *
864
     * @param  string|null $key     Optional data key to retrieve from the configset.
865
     * @param  mixed|null  $default The default value to return if data key does not exist.
866
     * @return mixed|AdminConfig
0 ignored issues
show
Bug introduced by
The type Charcoal\Admin\Action\AdminConfig was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
867
     */
868
    protected function elfinderConfig($key = null, $default = null)
869
    {
870
        if ($key) {
871
            if (isset($this->elfinderConfig[$key])) {
872
                return $this->elfinderConfig[$key];
873
            } else {
874
                if (!is_string($default) && is_callable($default)) {
875
                    return $default();
876
                } else {
877
                    return $default;
878
                }
879
            }
880
        }
881
882
        return $this->elfinderConfig;
883
    }
884
885
    /**
886
     * Set a property factory.
887
     *
888
     * @param FactoryInterface $factory The property factory,
889
     *                                  to createable property values.
890
     * @return void
891
     */
892
    protected function setPropertyFactory(FactoryInterface $factory)
893
    {
894
        $this->propertyFactory = $factory;
895
    }
896
897
    /**
898
     * Retrieve the property factory.
899
     *
900
     * @throws RuntimeException If the property factory was not previously set.
901
     * @return FactoryInterface
902
     */
903
    public function propertyFactory()
904
    {
905
        if (!isset($this->propertyFactory)) {
906
            throw new RuntimeException(sprintf(
907
                'Property Factory is not defined for "%s"',
908
                get_class($this)
909
            ));
910
        }
911
912
        return $this->propertyFactory;
913
    }
914
}
915