From 33f4517156f4d2ab934cc3a8e7ea211175c50a30 Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Sun, 19 Nov 2023 20:00:28 +1000 Subject: [PATCH 01/12] Removed funding and adjusted test ci --- .github/FUNDING.yml | 1 - .github/workflows/test.yaml | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) delete mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml deleted file mode 100644 index 98e9033..0000000 --- a/.github/FUNDING.yml +++ /dev/null @@ -1 +0,0 @@ -custom: "https://paypal.me/heliosag" diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 2574f6c..0f6a644 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -1,11 +1,11 @@ # OS: Linux; Symfony: latest stable; PHP: all the commonly used versions supported by this bundle -name: "Tests - Linux" +name: "Tests" on: pull_request: push: branches: - - 'master' + - 'main' env: fail-fast: true @@ -17,7 +17,7 @@ jobs: continue-on-error: false strategy: matrix: - php-version: ['7.4', '8.0'] + php-version: ['7.4', '8.0', '8.1', '8.2'] steps: - name: 'Checkout code' uses: actions/checkout@v2.3.3 From e4027caf5f2c02a9ca23e8531930aa015b9ce19a Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Sun, 19 Nov 2023 20:02:01 +1000 Subject: [PATCH 02/12] Updated ceckout and setup-php --- .github/workflows/test.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yaml b/.github/workflows/test.yaml index 0f6a644..7b763da 100644 --- a/.github/workflows/test.yaml +++ b/.github/workflows/test.yaml @@ -20,15 +20,15 @@ jobs: php-version: ['7.4', '8.0', '8.1', '8.2'] steps: - name: 'Checkout code' - uses: actions/checkout@v2.3.3 + uses: actions/checkout@v3 - name: 'Install PHP with extensions' - uses: shivammathur/setup-php@2.7.0 + uses: shivammathur/setup-php@2.27.1 with: coverage: none php-version: ${{ matrix.php-version }} tools: composer:v2 - extensions: mbstring, intl, pdo, pdo_sqlite, sqlite3 + extensions: mbstring, intl ini-values: date.timezone=UTC - name: 'Install project dependencies' From 3ed82e3dccbcfc8f9a78f435000354d265662aec Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Sun, 19 Nov 2023 20:02:25 +1000 Subject: [PATCH 03/12] Updated lowest elfinder due to CVE --- composer.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/composer.json b/composer.json index 40dd8e3..f9f0eb2 100644 --- a/composer.json +++ b/composer.json @@ -26,14 +26,14 @@ "symfony/twig-bundle": "^4.4 || ^5.0 || ^5.2 || ^6.0", "symfony/form": "^4.4 || ^5.0 || ^5.2 || ^6.0", "symfony/asset": "^4.4 || ^5.0 || ^5.2 || ^6.0", - "studio-42/elfinder": "~2.1" + "studio-42/elfinder": "~2.1.62" }, "require-dev" : { - "symfony/phpunit-bridge": "^4.1 || ^5.0 || ^5.2 || ^6.0", - "matthiasnoback/symfony-config-test": "^4.0", + "symfony/phpunit-bridge": "^4.1 || ^5.0 || ^5.2 || ^6.0", + "matthiasnoback/symfony-config-test": "^4.0", "matthiasnoback/symfony-dependency-injection-test": "^4.1", - "symfony/finder": "^4.4 || ^5.0 || ^5.2 || ^6.0", - "php-coveralls/php-coveralls": "^2.0" + "symfony/finder": "^4.4 || ^5.0 || ^5.2 || ^6.0", + "php-coveralls/php-coveralls": "^2.0" }, "suggest": { "helios-ag/fm-tinymce-bundle": "FMTinyMCEBundle WYSIWYG Editor Bundle", From 90964244e9d0171b606d7dce386497dc80e46eed Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Mon, 20 Nov 2023 09:48:32 +1000 Subject: [PATCH 04/12] Minor imprvements --- .coveralls.yml | 2 -- .github/dependabot.yml | 2 +- .github/workflows/changelog.yml | 30 ------------------------------ .github/workflows/phpstan.yaml | 2 +- .php_cs | 18 ++++++++++++++++++ tests/FMElfinderBundleTest.php | 2 +- 6 files changed, 21 insertions(+), 35 deletions(-) delete mode 100644 .coveralls.yml delete mode 100644 .github/workflows/changelog.yml create mode 100644 .php_cs diff --git a/.coveralls.yml b/.coveralls.yml deleted file mode 100644 index 90ae313..0000000 --- a/.coveralls.yml +++ /dev/null @@ -1,2 +0,0 @@ -service_name: travis-ci -coverage_clover: build/logs/clover.xml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index c630ffa..c9853a4 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -3,5 +3,5 @@ updates: - package-ecosystem: composer directory: "/" schedule: - interval: daily + interval: weekly open-pull-requests-limit: 10 diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml deleted file mode 100644 index f3de458..0000000 --- a/.github/workflows/changelog.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: Changelog - -on: push - -jobs: - generate_changelog: - runs-on: ubuntu-latest - steps: - - name: Generate change log - uses: heinrichreimer/github-changelog-generator-action@v2.1.1 - with: - token: ${{ secrets.GITHUB_TOKEN }} - headerLabel: "# Changelog" - breakingLabel: '### Breaking' - enhancementLabel: '### Enhancements' - stripGeneratorNotice: true - bugsLabel: '### Fixes' - issues: true - issuesWoLabels: true - pullRequests: true - prWoLabels: true - author: false - verbose: true - - name: "🖨️ Print changelog to console" - run: cat CHANGELOG.md - - name: "📤 Upload changelog" - uses: actions/upload-artifact@v1.0.0 - with: - name: "Changelog" - path: CHANGELOG.md \ No newline at end of file diff --git a/.github/workflows/phpstan.yaml b/.github/workflows/phpstan.yaml index b5b7c43..8765146 100644 --- a/.github/workflows/phpstan.yaml +++ b/.github/workflows/phpstan.yaml @@ -23,7 +23,7 @@ jobs: # Steps represent a sequence of tasks that will be executed as part of the job steps: # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 # Runs a single command using the runners shell - name: phpstan diff --git a/.php_cs b/.php_cs new file mode 100644 index 0000000..ca2f129 --- /dev/null +++ b/.php_cs @@ -0,0 +1,18 @@ +in(__DIR__) + ->exclude('vendor'); + +return Config::create() + ->setUsingCache(true) + ->setRules([ + '@Symfony' => true, + 'array_syntax' => ['syntax' => 'short'], + 'ordered_imports' => true, + 'yoda_style' => false, + ]) + ->setFinder($finder); \ No newline at end of file diff --git a/tests/FMElfinderBundleTest.php b/tests/FMElfinderBundleTest.php index 3f7bd11..b1d9124 100644 --- a/tests/FMElfinderBundleTest.php +++ b/tests/FMElfinderBundleTest.php @@ -22,6 +22,6 @@ public function testCompilerPasses() $bundle->build($containerBuilder); $passes = $containerBuilder->getCompilerPassConfig()->getBeforeOptimizationPasses(); - self::assertEquals(8, count($passes)); + self::assertEquals(9, count($passes)); } } From 968ae2ba017a4b38615d79b28ab63290e96e79db Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Mon, 20 Nov 2023 12:56:47 +1000 Subject: [PATCH 05/12] Added devcontainer --- .devcontainer/devcontainer.json | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..37e756b --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,38 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/php +{ + "name": "PHP", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/php:0-8.2", + + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + "settings": {}, + "extensions": [ + "streetsidesoftware.code-spell-checker" + ] + } + }, + + // Use 'forwardPorts' to make a list of ports inside the container available locally. + // "forwardPorts": [8000], + + // Use 'portsAttributes' to set default properties for specific forwarded ports. More info: https://code.visualstudio.com/docs/remote/devcontainerjson-reference. + "portsAttributes": { + "8000": { + "label": "Hello Remote World", + "onAutoForward": "notify" + } + } + + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "sudo chmod a+x \"$(pwd)\" && sudo rm -rf /var/www/html && sudo ln -s \"$(pwd)\" /var/www/html" + + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} \ No newline at end of file From 19a4fe2ca1d2b333a1d9bd5a34b03584716e29ac Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Mon, 20 Nov 2023 07:55:40 +0000 Subject: [PATCH 06/12] Updated symfony bundle --- .symfony.bundle.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.symfony.bundle.yaml b/.symfony.bundle.yaml index 876e3de..611b563 100644 --- a/.symfony.bundle.yaml +++ b/.symfony.bundle.yaml @@ -1,6 +1,6 @@ -branches: ["master"] -maintained_branches: ["master"] -current_branch: "master" -dev_branch: "master" +branches: ["main"] +maintained_branches: ["main"] +current_branch: "main" +dev_branch: "main" dev_branch_alias: "12.x" doc_dir: "docs/" From 66edf6188612c6d1a978f0bacfbf7e463dd3bb2e Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Mon, 20 Nov 2023 07:55:58 +0000 Subject: [PATCH 07/12] Migrated phpunt --- phpunit.xml.dist | 74 +++++++++++++++++++++++------------------------- 1 file changed, 35 insertions(+), 39 deletions(-) diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 4b67831..fb1c3b7 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,41 +1,37 @@ - - - - - - - - - - - - - - - ./tests - - - - - - ./ - - ./src/Controller - ./src/Resources - ./tests - ./vendor - - - - - - - - + + + + ./ + + + ./src/Controller + ./src/Resources + ./tests + ./vendor + + + + + + + + + + + + + + + ./tests + + + + + From d24e42ddb21fed0ac086ceaf694a9cef7a439f95 Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Mon, 20 Nov 2023 18:08:30 +1000 Subject: [PATCH 08/12] Added csfixer --- .php-cs-fixer.php | 84 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 .php-cs-fixer.php diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..621e2b5 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,84 @@ +setRiskyAllowed(false) + ->setRules([ + '@Symfony' => true, + '@PHP80Migration' => true, + 'array_syntax' => ['syntax' => 'short'], + 'combine_consecutive_unsets' => true, + // one should use PHPUnit methods to set up expected exception instead of annotations + 'general_phpdoc_annotation_remove' => [ + 'annotations' => [ + 'author', + 'package', + 'expectedException', + 'expectedExceptionMessage', + 'expectedExceptionMessageRegExp', + ], + ], + 'function_typehint_space' => false, + 'no_empty_phpdoc' => true, + 'global_namespace_import' => ['import_classes' => true, 'import_functions' => true, 'import_constants' => true], + 'no_superfluous_phpdoc_tags' => ['allow_mixed' => false, 'allow_unused_params' => false], + 'phpdoc_line_span' => ['property' => 'single'], + 'heredoc_to_nowdoc' => true, + 'list_syntax' => ['syntax' => 'short'], + 'blank_line_before_statement' => ['statements' => ['if', 'break', 'continue', 'declare', 'return', 'throw', 'try', 'yield']], + 'no_extra_blank_lines' => [ + 'tokens' => [ + 'break', + 'continue', + 'extra', + 'return', + 'throw', + 'use', + 'parenthesis_brace_block', + 'square_brace_block', + 'curly_brace_block', + ], + ], + 'echo_tag_syntax' => true, + 'method_argument_space' => false, + 'no_useless_else' => true, + 'no_useless_return' => true, + 'ordered_class_elements' => true, + 'ordered_imports' => ['sort_algorithm' => 'alpha', 'imports_order' => ['const', 'class', 'function']], + 'php_unit_test_class_requires_covers' => true, + 'phpdoc_align' => [ + 'tags' => [ + 'param', 'return', 'throws', 'type', 'var' + ], + ], + 'phpdoc_add_missing_param_annotation' => true, + 'phpdoc_order' => true, + 'phpdoc_no_alias_tag' => ['replacements' => ['link' => 'website']], + 'phpdoc_summary' => false, + 'phpdoc_to_comment' => false, + 'phpdoc_types_order' => false, // breaks psalm-specific notation sometimes! + 'semicolon_after_instruction' => true, + 'single_blank_line_at_eof' => true, + 'single_line_throw' => false, + 'types_spaces' => false, + 'binary_operator_spaces' => [ + 'default' => 'single_space', + 'operators' => [ + '=' => 'align_single_space_minimal', + '-=' => 'align_single_space_minimal', + '+=' => 'align_single_space_minimal', + '=>' => 'align_single_space_minimal', + '===' => null, + '??=' => 'align_single_space_minimal', + ], + ], + 'concat_space' => [ + 'spacing' => 'one', + ], + 'operator_linebreak' => ['only_booleans' => true, 'position' => 'end'], + 'yoda_style' => false, + ]) + ->setFinder( + PhpCsFixer\Finder::create() + ->in(__DIR__ . '/src') + ) +; From ea8ae497a496193ab27dfce288e0b890abc9309c Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Mon, 20 Nov 2023 18:08:42 +1000 Subject: [PATCH 09/12] Updated gitignore --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 50553fe..dc79fa3 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,6 @@ Tests/cache Tests/log Tests/Functional/cache Tests/Functional/log +vendor +.phpunit.result.cache +.php-cs-fixer.cache \ No newline at end of file From d35d8f9c8b9a9f655824d81b1c6fd65607fea9e0 Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Mon, 20 Nov 2023 18:08:56 +1000 Subject: [PATCH 10/12] Dropped stan --- .github/workflows/phpstan.yaml | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 .github/workflows/phpstan.yaml diff --git a/.github/workflows/phpstan.yaml b/.github/workflows/phpstan.yaml deleted file mode 100644 index 8765146..0000000 --- a/.github/workflows/phpstan.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# This is a basic workflow to help you get started with Actions - -name: PHPStan - -# Controls when the action will run. -on: - # Triggers the workflow on push or pull request events but only for the master branch - push: - branches: [ '**' ] - pull_request: - branches: [ '**' ] - - # Allows you to run this workflow manually from the Actions tab - workflow_dispatch: - -# A workflow run is made up of one or more jobs that can run sequentially or in parallel -jobs: - # This workflow contains a single job called "build" - build: - # The type of runner that the job will run on - runs-on: ubuntu-latest - - # Steps represent a sequence of tasks that will be executed as part of the job - steps: - # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it - - uses: actions/checkout@v3 - - # Runs a single command using the runners shell - - name: phpstan - uses: OskarStark/phpstan-ga@0.12.77 From d29160488ccddf32616edfca2181647966a91c23 Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Mon, 20 Nov 2023 18:09:03 +1000 Subject: [PATCH 11/12] CS fixer dep --- composer.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/composer.json b/composer.json index f9f0eb2..e045fa6 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,8 @@ "matthiasnoback/symfony-config-test": "^4.0", "matthiasnoback/symfony-dependency-injection-test": "^4.1", "symfony/finder": "^4.4 || ^5.0 || ^5.2 || ^6.0", - "php-coveralls/php-coveralls": "^2.0" + "php-coveralls/php-coveralls": "^2.0", + "friendsofphp/php-cs-fixer": "dev-master" }, "suggest": { "helios-ag/fm-tinymce-bundle": "FMTinyMCEBundle WYSIWYG Editor Bundle", From d4fd1999962d1a42b0c285c49e54657921a30267 Mon Sep 17 00:00:00 2001 From: Al Ganiev Date: Mon, 20 Nov 2023 18:09:40 +1000 Subject: [PATCH 12/12] CS Applied --- .php_cs | 18 -- composer.json | 2 +- src/Bridge/ElFinderBridge.php | 24 +- src/Command/ElFinderInstallerCommand.php | 29 +-- .../ElFinderConfigurationReader.php | 230 +++++++++--------- src/Connector/ElFinderConnector.php | 15 +- src/Controller/ElFinderController.php | 119 ++++----- .../Compiler/ElFinderConfigurationPass.php | 11 +- .../Compiler/TwigFormPass.php | 2 +- src/DependencyInjection/Configuration.php | 5 +- .../FMElfinderExtension.php | 4 +- src/ElFinder/ElFinder.php | 54 ++-- src/Event/ElFinderPreExecutionEvent.php | 14 +- src/Form/Type/ElFinderType.php | 10 +- src/Loader/ElFinderLoader.php | 20 +- src/Session/ElFinderSession.php | 3 +- src/Twig/Extension/FMElfinderExtension.php | 2 - tests/FMElfinderBundleTest.php | 10 - 18 files changed, 289 insertions(+), 283 deletions(-) delete mode 100644 .php_cs diff --git a/.php_cs b/.php_cs deleted file mode 100644 index ca2f129..0000000 --- a/.php_cs +++ /dev/null @@ -1,18 +0,0 @@ -in(__DIR__) - ->exclude('vendor'); - -return Config::create() - ->setUsingCache(true) - ->setRules([ - '@Symfony' => true, - 'array_syntax' => ['syntax' => 'short'], - 'ordered_imports' => true, - 'yoda_style' => false, - ]) - ->setFinder($finder); \ No newline at end of file diff --git a/composer.json b/composer.json index e045fa6..ec8ca56 100644 --- a/composer.json +++ b/composer.json @@ -34,7 +34,7 @@ "matthiasnoback/symfony-dependency-injection-test": "^4.1", "symfony/finder": "^4.4 || ^5.0 || ^5.2 || ^6.0", "php-coveralls/php-coveralls": "^2.0", - "friendsofphp/php-cs-fixer": "dev-master" + "friendsofphp/php-cs-fixer": "dev-master" }, "suggest": { "helios-ag/fm-tinymce-bundle": "FMTinyMCEBundle WYSIWYG Editor Bundle", diff --git a/src/Bridge/ElFinderBridge.php b/src/Bridge/ElFinderBridge.php index 06ddaaf..0f0405e 100644 --- a/src/Bridge/ElFinderBridge.php +++ b/src/Bridge/ElFinderBridge.php @@ -2,9 +2,9 @@ namespace FM\ElfinderBundle\Bridge; -use Symfony\Component\HttpFoundation\Session\SessionInterface; -use FM\ElfinderBundle\ElFinder\ElFinder; use elFinderVolumeDriver; +use FM\ElfinderBundle\ElFinder\ElFinder; +use Symfony\Component\HttpFoundation\Session\SessionInterface; class ElFinderBridge extends ElFinder { @@ -19,12 +19,19 @@ public function __construct($opts) parent::__construct($opts); } - /** @param $session */ public function setSession($session) { $this->session = $session; } + /** + * @return array + */ + public function getVolumes() + { + return $this->volumes; + } + /** * @param array $opts */ @@ -32,8 +39,10 @@ protected function mountVolumes($opts) { foreach ($opts['roots'] as $i => $o) { $volume = null; + if (isset($o['service'])) { $driver = $o['service']; + if (is_object($driver) && $driver instanceof elFinderVolumeDriver) { $volume = $driver; unset($opts['roots'][$i]); @@ -45,6 +54,7 @@ protected function mountVolumes($opts) $id = $volume->id(); $this->volumes[$id] = $volume; + if (!$this->default && $volume->isReadable()) { $this->default = $this->volumes[$id]; } @@ -52,12 +62,4 @@ protected function mountVolumes($opts) } parent::mountVolumes($opts); } - - /** - * @return array - */ - public function getVolumes() - { - return $this->volumes; - } } diff --git a/src/Command/ElFinderInstallerCommand.php b/src/Command/ElFinderInstallerCommand.php index 94647e9..d1e1751 100644 --- a/src/Command/ElFinderInstallerCommand.php +++ b/src/Command/ElFinderInstallerCommand.php @@ -2,6 +2,7 @@ namespace FM\ElfinderBundle\Command; +use ReflectionClass; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; @@ -12,13 +13,13 @@ final class ElFinderInstallerCommand extends Command { - private const ELFINDER_CSS_DIR = 'vendor/studio-42/elfinder/css'; + private const ELFINDER_CSS_DIR = 'vendor/studio-42/elfinder/css'; - private const ELFINDER_JS_DIR = 'vendor/studio-42/elfinder/js'; + private const ELFINDER_JS_DIR = 'vendor/studio-42/elfinder/js'; private const ELFINDER_SOUNDS_DIR = 'vendor/studio-42/elfinder/sounds'; - private const ELFINDER_IMG_DIR = 'vendor/studio-42/elfinder/img'; + private const ELFINDER_IMG_DIR = 'vendor/studio-42/elfinder/img'; protected $fileSystem; @@ -38,13 +39,13 @@ protected function configure(): void ->setDescription('Copies elfinder assets to public directory') ->addOption('docroot', null, InputOption::VALUE_OPTIONAL, 'Website document root.', 'public') ->setHelp(<<<'EOF' -Default docroot: - public + Default docroot: + public -You can pass docroot: - Where to install elfinder - php %command.full_name% --docroot=public_html -EOF + You can pass docroot: + Where to install elfinder + php %command.full_name% --docroot=public_html + EOF ); } @@ -59,15 +60,15 @@ protected function execute(InputInterface $input, OutputInterface $output): int $publicDir = sprintf('%s/%s/bundles/fmelfinder', $rootDir, $dr); - $reflection = new \ReflectionClass(\Composer\Autoload\ClassLoader::class); + $reflection = new ReflectionClass(\Composer\Autoload\ClassLoader::class); $vendorRootDir = dirname($reflection->getFileName(), 3); $io->note(sprintf('Starting to install elfinder to %s folder', $publicDir)); - $this->fileSystem->mirror($vendorRootDir.'/'.self::ELFINDER_CSS_DIR, $publicDir.'/css'); - $this->fileSystem->mirror($vendorRootDir.'/'.self::ELFINDER_IMG_DIR, $publicDir.'/img'); - $this->fileSystem->mirror($vendorRootDir.'/'.self::ELFINDER_JS_DIR, $publicDir.'/js'); - $this->fileSystem->mirror($vendorRootDir.'/'.self::ELFINDER_SOUNDS_DIR, $publicDir.'/sounds'); + $this->fileSystem->mirror($vendorRootDir . '/' . self::ELFINDER_CSS_DIR, $publicDir . '/css'); + $this->fileSystem->mirror($vendorRootDir . '/' . self::ELFINDER_IMG_DIR, $publicDir . '/img'); + $this->fileSystem->mirror($vendorRootDir . '/' . self::ELFINDER_JS_DIR, $publicDir . '/js'); + $this->fileSystem->mirror($vendorRootDir . '/' . self::ELFINDER_SOUNDS_DIR, $publicDir . '/sounds'); $io->success('elFinder assets successfully installed'); diff --git a/src/Configuration/ElFinderConfigurationReader.php b/src/Configuration/ElFinderConfigurationReader.php index f08f03e..1983594 100644 --- a/src/Configuration/ElFinderConfigurationReader.php +++ b/src/Configuration/ElFinderConfigurationReader.php @@ -2,29 +2,31 @@ namespace FM\ElfinderBundle\Configuration; +use Aws\S3\S3Client; +use Barracuda\Copy\API; +use Exception; use FM\ElfinderBundle\Security\ElfinderSecurityInterface; -use League\Flysystem\AdapterInterface; -use Symfony\Component\DependencyInjection\ContainerInterface; -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\HttpFoundation\RequestStack; -use League\Flysystem\Filesystem; -use League\Flysystem\Adapter\Local; use League\Flysystem\Adapter\Ftp; -use Spatie\FlysystemDropbox\DropboxAdapter; -use League\Flysystem\Sftp\SftpAdapter; +use League\Flysystem\Adapter\Local; +use League\Flysystem\AdapterInterface; use League\Flysystem\AwsS3v2\AwsS3Adapter as AwsS3v2; use League\Flysystem\AwsS3V3\AwsS3V3Adapter as AwsS3v3; +use League\Flysystem\AzureBlobStorage\AzureBlobStorageAdapter; +use League\Flysystem\Copy\CopyAdapter; +use League\Flysystem\Filesystem; use League\Flysystem\GridFS\GridFSAdapter; -use OpenCloud\Rackspace; use League\Flysystem\Rackspace\RackspaceAdapter; -use MongoClient; -use League\Flysystem\Copy\CopyAdapter; +use League\Flysystem\Sftp\SftpAdapter; use League\Flysystem\ZipArchive\ZipArchiveAdapter; -use Aws\S3\S3Client; -use Spatie\Dropbox\Client; -use Barracuda\Copy\API; -use League\Flysystem\AzureBlobStorage\AzureBlobStorageAdapter; use MicrosoftAzure\Storage\Blob\BlobRestProxy; +use MongoClient; +use OpenCloud\Rackspace; +use Spatie\Dropbox\Client; +use Spatie\FlysystemDropbox\DropboxAdapter; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\RequestStack; +use TypeError; /** * Class ElFinderConfigurationReader. @@ -81,45 +83,45 @@ public function getConfiguration(string $instance): array $driver = $this->container->has($parameter['driver']) ? $this->container->get($parameter['driver']) : false; $driverOptions = [ - 'driver' => $parameter['driver'], - 'service' => $driver, - 'glideURL' => $parameter['glide_url'], - 'glideKey' => $parameter['glide_key'], - 'plugin' => $options['plugin'], - 'path' => $pathAndHomeFolder, - 'startPath' => $parameter['start_path'], - 'encoding' => $parameter['encoding'], - 'URL' => $this->getURL($parameter, $request, $homeFolder, $path), - 'alias' => $parameter['alias'], - 'mimeDetect' => $parameter['mime_detect'], - 'mimefile' => $parameter['mimefile'], - 'imgLib' => $parameter['img_lib'], - 'tmbPath' => $parameter['tmb_path'], - 'tmbPathMode' => $parameter['tmb_path_mode'], - 'tmbURL' => $parameter['tmb_url'], - 'tmbSize' => $parameter['tmb_size'], - 'tmbCrop' => $parameter['tmb_crop'], - 'tmbBgColor' => $parameter['tmb_bg_color'], - 'copyOverwrite' => $parameter['copy_overwrite'], - 'copyJoin' => $parameter['copy_join'], - 'copyFrom' => $parameter['copy_from'], - 'copyTo' => $parameter['copy_to'], - 'uploadOverwrite' => $parameter['upload_overwrite'], - 'uploadAllow' => $parameter['upload_allow'], - 'uploadDeny' => $parameter['upload_deny'], - 'uploadMaxSize' => $parameter['upload_max_size'], - 'uploadMaxConn' => $parameter['upload_max_conn'], - 'defaults' => $parameter['defaults'], - 'attributes' => $parameter['attributes'], - 'acceptedName' => $parameter['accepted_name'], - 'disabled' => $parameter['disabled_commands'], - 'treeDeep' => $parameter['tree_deep'], - 'checkSubfolders' => $parameter['check_subfolders'], - 'separator' => $parameter['separator'], - 'timeFormat' => $parameter['time_format'], - 'archiveMimes' => $parameter['archive_mimes'], - 'archivers' => $parameter['archivers'], - 'fileMode' => $parameter['fileMode'], + 'driver' => $parameter['driver'], + 'service' => $driver, + 'glideURL' => $parameter['glide_url'], + 'glideKey' => $parameter['glide_key'], + 'plugin' => $options['plugin'], + 'path' => $pathAndHomeFolder, + 'startPath' => $parameter['start_path'], + 'encoding' => $parameter['encoding'], + 'URL' => $this->getURL($parameter, $request, $homeFolder, $path), + 'alias' => $parameter['alias'], + 'mimeDetect' => $parameter['mime_detect'], + 'mimefile' => $parameter['mimefile'], + 'imgLib' => $parameter['img_lib'], + 'tmbPath' => $parameter['tmb_path'], + 'tmbPathMode' => $parameter['tmb_path_mode'], + 'tmbURL' => $parameter['tmb_url'], + 'tmbSize' => $parameter['tmb_size'], + 'tmbCrop' => $parameter['tmb_crop'], + 'tmbBgColor' => $parameter['tmb_bg_color'], + 'copyOverwrite' => $parameter['copy_overwrite'], + 'copyJoin' => $parameter['copy_join'], + 'copyFrom' => $parameter['copy_from'], + 'copyTo' => $parameter['copy_to'], + 'uploadOverwrite' => $parameter['upload_overwrite'], + 'uploadAllow' => $parameter['upload_allow'], + 'uploadDeny' => $parameter['upload_deny'], + 'uploadMaxSize' => $parameter['upload_max_size'], + 'uploadMaxConn' => $parameter['upload_max_conn'], + 'defaults' => $parameter['defaults'], + 'attributes' => $parameter['attributes'], + 'acceptedName' => $parameter['accepted_name'], + 'disabled' => $parameter['disabled_commands'], + 'treeDeep' => $parameter['tree_deep'], + 'checkSubfolders' => $parameter['check_subfolders'], + 'separator' => $parameter['separator'], + 'timeFormat' => $parameter['time_format'], + 'archiveMimes' => $parameter['archive_mimes'], + 'archivers' => $parameter['archivers'], + 'fileMode' => $parameter['fileMode'], ]; if (null !== $parameter['quarantine']) { @@ -150,6 +152,42 @@ public function getConfiguration(string $instance): array return $options; } + /** + * Simple function to demonstrate how to control file access using "accessControl" callback. + * This method will disable accessing files/folders starting from '.' (dot). + * + * @param string $attr attribute name (read|write|locked|hidden) + * @param string $path file path relative to volume root directory started with directory separator + * + * @return bool|null + */ + public function access($attr, $path, $data, $volume) + { + return 0 === strpos(basename($path), '.') // if file/folder begins with '.' (dot) + ? !('read' == $attr || 'write' == $attr) // set read+write to false, other (locked+hidden) set to true + : null; // else elFinder decide it itself + } + + /** + * @return array + */ + protected function parseSecurityConfiguration(ElfinderSecurityInterface $voter) + { + $configuration = $voter->getConfiguration(); + + if (!is_array($configuration)) { + throw new Exception('ElfinderSecurityVoter should return array'); + } + + foreach ($configuration as $role => $commands) { + if ($this->container->get('security.authorization_checker')->isGranted($role)) { + return $commands; + } + } + + return []; + } + private function getURL(array $parameter, Request $request, string $homeFolder, string $path): string { if (isset($parameter['url']) && $parameter['url']) { @@ -157,19 +195,15 @@ private function getURL(array $parameter, Request $request, string $homeFolder, return str_replace('{homeFolder}', $homeFolder, $parameter['url']); } - $path = $parameter['url'].'/'.$homeFolder; + $path = $parameter['url'] . '/' . $homeFolder; } else { - $path = $path.'/'.$homeFolder; + $path = $path . '/' . $homeFolder; } - return $request->getUriForPath('/'.trim($path, '/')); + return $request->getUriForPath('/' . trim($path, '/')); } /** - * @param $opt - * @param $adapter - * @param $serviceName - * * @return Filesystem */ private function configureFlysystem($opt, $adapter, $serviceName) @@ -220,10 +254,11 @@ private function configureFlysystem($opt, $adapter, $serviceName) break; case 'aws_s3_v2': $options = [ - 'key' => $opt['aws_s3_v2']['key'], - 'secret' => $opt['aws_s3_v2']['secret'], - 'region' => $opt['aws_s3_v2']['region'], + 'key' => $opt['aws_s3_v2']['key'], + 'secret' => $opt['aws_s3_v2']['secret'], + 'region' => $opt['aws_s3_v2']['region'], ]; + if (isset($opt['aws_s3_v2']['base_url']) && $opt['aws_s3_v2']['base_url']) { $options['base_url'] = $opt['aws_s3_v2']['base_url']; } @@ -238,6 +273,7 @@ private function configureFlysystem($opt, $adapter, $serviceName) 'endpoint' => $opt['aws_s3_v3']['endpoint'], 'use_path_style_endpoint' => $opt['aws_s3_v3']['use_path_style_endpoint'], ]; + if (!empty($opt['aws_s3_v3']['key']) && !empty($opt['aws_s3_v3']['secret'])) { $s3Options['credentials'] = [ 'key' => $opt['aws_s3_v3']['key'], @@ -284,10 +320,11 @@ private function configureFlysystem($opt, $adapter, $serviceName) break; case 'custom': $adapter = $this->container->get($serviceName); + try { $filesystem = new Filesystem($adapter); - } catch (\TypeError $error) { - throw new \Exception(sprintf('Service %s is not an instance of %s.', $serviceName, AdapterInterface::class)); + } catch (TypeError $error) { + throw new Exception(sprintf('Service %s is not an instance of %s.', $serviceName, AdapterInterface::class)); } break; @@ -299,8 +336,9 @@ private function configureFlysystem($opt, $adapter, $serviceName) private function getFlysystemFilesystem(string $serviceName) { $filesystem = $this->container->get($serviceName); + if (!is_object($filesystem) || (!$filesystem instanceof Filesystem)) { - throw new \Exception(sprintf('Service %s is not an instance of %s.', $serviceName, Filesystem::class)); + throw new Exception(sprintf('Service %s is not an instance of %s.', $serviceName, Filesystem::class)); } return $filesystem; @@ -362,16 +400,16 @@ private function configureDriver(array $parameter): array break; case 'box': - $settings['client_id'] = $parameter['box_settings']['client_id']; - $settings['client_secret'] = $parameter['box_settings']['client_secret']; - $settings['accessToken'] = $parameter['box_settings']['accessToken']; - $settings['root'] = $parameter['box_settings']['root']; - $settings['path'] = $parameter['box_settings']['path']; - $settings['separator'] = $parameter['box_settings']['separator']; - $settings['tmbPath'] = $parameter['box_settings']['tmbPath']; - $settings['tmbURL'] = $parameter['box_settings']['tmbURL']; - $settings['acceptedName'] = $parameter['box_settings']['acceptedName']; - $settings['rootCssClass'] = $parameter['box_settings']['rootCssClass']; + $settings['client_id'] = $parameter['box_settings']['client_id']; + $settings['client_secret'] = $parameter['box_settings']['client_secret']; + $settings['accessToken'] = $parameter['box_settings']['accessToken']; + $settings['root'] = $parameter['box_settings']['root']; + $settings['path'] = $parameter['box_settings']['path']; + $settings['separator'] = $parameter['box_settings']['separator']; + $settings['tmbPath'] = $parameter['box_settings']['tmbPath']; + $settings['tmbURL'] = $parameter['box_settings']['tmbURL']; + $settings['acceptedName'] = $parameter['box_settings']['acceptedName']; + $settings['rootCssClass'] = $parameter['box_settings']['rootCssClass']; break; default: @@ -380,42 +418,4 @@ private function configureDriver(array $parameter): array return $settings; } - - /** - * Simple function to demonstrate how to control file access using "accessControl" callback. - * This method will disable accessing files/folders starting from '.' (dot). - * - * @param string $attr attribute name (read|write|locked|hidden) - * @param string $path file path relative to volume root directory started with directory separator - * @param $data - * @param $volume - * - * @return bool|null - */ - public function access($attr, $path, $data, $volume) - { - return 0 === strpos(basename($path), '.') // if file/folder begins with '.' (dot) - ? !('read' == $attr || 'write' == $attr) // set read+write to false, other (locked+hidden) set to true - : null; // else elFinder decide it itself - } - - /** - * @return array - */ - protected function parseSecurityConfiguration(ElfinderSecurityInterface $voter) - { - $configuration = $voter->getConfiguration(); - - if (!is_array($configuration)) { - throw new \Exception('ElfinderSecurityVoter should return array'); - } - - foreach ($configuration as $role => $commands) { - if ($this->container->get('security.authorization_checker')->isGranted($role)) { - return $commands; - } - } - - return []; - } } diff --git a/src/Connector/ElFinderConnector.php b/src/Connector/ElFinderConnector.php index d60ca2e..f04c82a 100644 --- a/src/Connector/ElFinderConnector.php +++ b/src/Connector/ElFinderConnector.php @@ -11,6 +11,7 @@ public function run($queryParameters = null) if (null === $queryParameters) { $queryParameters = $_GET; } + return $this->execute($queryParameters); } @@ -18,23 +19,24 @@ public function execute($queryParameters) { $isPost = 'POST' == $_SERVER['REQUEST_METHOD']; $src = 'POST' == $_SERVER['REQUEST_METHOD'] ? array_merge($_POST, $queryParameters) : $queryParameters; + if ($isPost && !$src && $rawPostData = @file_get_contents('php://input')) { // for support IE XDomainRequest() $parts = explode('&', $rawPostData); foreach ($parts as $part) { - list($key, $value) = array_pad(explode('=', $part), 2, ''); - $src[$key] = rawurldecode($value); + [$key, $value] = array_pad(explode('=', $part), 2, ''); + $src[$key] = rawurldecode($value); } $_POST = $src; $_REQUEST = array_merge_recursive($src, $_REQUEST); } - $cmd = isset($src['cmd']) ? $src['cmd'] : ''; - $args = []; + $cmd = $src['cmd'] ?? ''; + $args = []; if (!function_exists('json_encode')) { $error = $this->elFinder->error(elFinder::ERROR_CONF, elFinder::ERROR_CONF_NO_JSON); - return $this->output(['error' => '{"error":["'.implode('","', $error).'"]}', 'raw' => true]); + return $this->output(['error' => '{"error":["' . implode('","', $error) . '"]}', 'raw' => true]); } if (!$this->elFinder->loaded()) { @@ -55,11 +57,12 @@ public function execute($queryParameters) foreach ($this->elFinder->commandArgsList($cmd) as $name => $req) { $arg = 'FILES' == $name ? $_FILES - : (isset($src[$name]) ? $src[$name] : ''); + : ($src[$name] ?? ''); if (!is_array($arg)) { $arg = trim($arg); } + if ($req && (!isset($arg) || '' === $arg)) { return $this->output(['error' => $this->elFinder->error(elFinder::ERROR_INV_PARAMS, $cmd)]); } diff --git a/src/Controller/ElFinderController.php b/src/Controller/ElFinderController.php index ae95496..044ec35 100644 --- a/src/Controller/ElFinderController.php +++ b/src/Controller/ElFinderController.php @@ -3,18 +3,18 @@ namespace FM\ElfinderBundle\Controller; use Exception; +use FM\ElfinderBundle\Event\ElFinderPostExecutionEvent; +use FM\ElfinderBundle\Event\ElFinderPreExecutionEvent; use FM\ElfinderBundle\Loader\ElFinderLoader; use FM\ElfinderBundle\Session\ElFinderSession; use Symfony\Component\Asset\Package; use Symfony\Component\Asset\VersionStrategy\EmptyVersionStrategy; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\JsonResponse; use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpFoundation\Session\SessionInterface; use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; -use FM\ElfinderBundle\Event\ElFinderPreExecutionEvent; -use FM\ElfinderBundle\Event\ElFinderPostExecutionEvent; use Symfony\Component\HttpKernel\HttpKernelInterface; use Twig\Environment; @@ -43,34 +43,71 @@ public function show(Request $request, string $instance, string $homeFolder): Re if (empty($efParameters['instances'][$instance])) { throw new NotFoundHttpException('Instance not found'); } - $parameters = $efParameters['instances'][$instance]; + $parameters = $efParameters['instances'][$instance]; if (empty($parameters['locale'])) { $parameters['locale'] = $request->getLocale(); } - $assetsPath = $efParameters['assets_path']; - $result = $this->selectEditor($parameters, $instance, $homeFolder, $assetsPath, $request->get('id')); + $assetsPath = $efParameters['assets_path']; + $result = $this->selectEditor($parameters, $instance, $homeFolder, $assetsPath, $request->get('id')); return new Response($this->twig->render($result['template'], $result['params'])); } + public function load(SessionInterface $session, HttpKernelInterface $httpKernel, EventDispatcherInterface $eventDispatcher, Request $request, string $instance, string $homeFolder): JsonResponse + { + $loader = $this->loader; + $efParameters = $this->params; + $loader->initBridge($instance, $efParameters); // builds up the Bridge object for the loader with the given instance + + if ($loader instanceof ElFinderLoader) { + $loader->setSession(new ElFinderSession($session)); + } + + $preExecutionEvent = new ElFinderPreExecutionEvent($request, $httpKernel, $instance, $homeFolder); + $eventDispatcher->dispatch($preExecutionEvent); + + $result = $loader->load($request); // the instance is already set + + $postExecutionEvent = new ElFinderPostExecutionEvent($request, $httpKernel, $instance, $homeFolder, $result); + $eventDispatcher->dispatch($postExecutionEvent); + + // returning result (who may have been modified by a post execution event listener) + return new JsonResponse($postExecutionEvent->getResult()); + } + + public function mainJS() + { + $version = new EmptyVersionStrategy(); + $package = new Package($version); + $mainUrl = $package->getUrl('/bundles/fmelfinder/js'); + + return new Response( + $this->twig->render('@FMElfinder/Elfinder/helper/main.js.twig',['mainUrl' => $mainUrl]), + 200, + [ + 'Content-type' => 'text/javascript', + ] + ); + } + /** * @throws Exception */ private function selectEditor(array $parameters, string $instance, string $homeFolder, string $assetsPath, string $formTypeId = null): array { - $editor = $parameters['editor']; - $locale = $parameters['locale'] ?: $this->container->getParameter('locale'); - $fullScreen = $parameters['fullscreen']; - $relativePath = $parameters['relative_path']; - $pathPrefix = $parameters['path_prefix']; - $theme = $parameters['theme']; + $editor = $parameters['editor']; + $locale = $parameters['locale'] ?: $this->container->getParameter('locale'); + $fullScreen = $parameters['fullscreen']; + $relativePath = $parameters['relative_path']; + $pathPrefix = $parameters['path_prefix']; + $theme = $parameters['theme']; // convert to javascript array - $onlyMimes = count($parameters['visible_mime_types']) - ? "['".implode("','", $parameters['visible_mime_types'])."']" + $onlyMimes = count($parameters['visible_mime_types']) + ? "['" . implode("','", $parameters['visible_mime_types']) . "']" : '[]'; - $result = []; + $result = []; switch ($editor) { case 'custom': @@ -184,53 +221,17 @@ private function selectEditor(array $parameters, string $instance, string $homeF default: $result['template'] = '@FMElfinder/Elfinder/simple.html.twig'; $result['params'] = [ - 'locale' => $locale, - 'fullscreen' => $fullScreen, - 'instance' => $instance, - 'homeFolder' => $homeFolder, - 'prefix' => $assetsPath, - 'onlyMimes' => $onlyMimes, - 'theme' => $theme, - 'pathPrefix' => $pathPrefix, + 'locale' => $locale, + 'fullscreen' => $fullScreen, + 'instance' => $instance, + 'homeFolder' => $homeFolder, + 'prefix' => $assetsPath, + 'onlyMimes' => $onlyMimes, + 'theme' => $theme, + 'pathPrefix' => $pathPrefix, ]; return $result; } } - - public function load(SessionInterface $session, HttpKernelInterface $httpKernel, EventDispatcherInterface $eventDispatcher, Request $request, string $instance, string $homeFolder): JsonResponse - { - $loader = $this->loader; - $efParameters = $this->params; - $loader->initBridge($instance, $efParameters); // builds up the Bridge object for the loader with the given instance - - if ($loader instanceof ElFinderLoader) { - $loader->setSession(new ElFinderSession($session)); - } - - $preExecutionEvent = new ElFinderPreExecutionEvent($request, $httpKernel, $instance, $homeFolder); - $eventDispatcher->dispatch($preExecutionEvent); - - $result = $loader->load($request); // the instance is already set - - $postExecutionEvent = new ElFinderPostExecutionEvent($request, $httpKernel, $instance, $homeFolder, $result); - $eventDispatcher->dispatch($postExecutionEvent); - - // returning result (who may have been modified by a post execution event listener) - return new JsonResponse($postExecutionEvent->getResult()); - } - - public function mainJS() - { - $version = new EmptyVersionStrategy(); - $package = new Package($version); - $mainUrl = $package->getUrl('/bundles/fmelfinder/js'); - return new Response( - $this->twig->render('@FMElfinder/Elfinder/helper/main.js.twig',['mainUrl' => $mainUrl]), - 200, - [ - 'Content-type' => 'text/javascript', - ] - ); - } } diff --git a/src/DependencyInjection/Compiler/ElFinderConfigurationPass.php b/src/DependencyInjection/Compiler/ElFinderConfigurationPass.php index 7180f14..379fe30 100644 --- a/src/DependencyInjection/Compiler/ElFinderConfigurationPass.php +++ b/src/DependencyInjection/Compiler/ElFinderConfigurationPass.php @@ -2,10 +2,15 @@ namespace FM\ElfinderBundle\DependencyInjection\Compiler; +use const E_USER_DEPRECATED; + use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\EventDispatcher\DependencyInjection\RegisterListenersPass; +use function count; +use function trigger_error; + final class ElFinderConfigurationPass implements CompilerPassInterface { public function process(ContainerBuilder $container): void @@ -19,14 +24,14 @@ public function process(ContainerBuilder $container): void $subscribers = $container->findTaggedServiceIds('fm_elfinder.subscriber'); foreach ($listeners as $serviceId => $tags) { - @\trigger_error('Using "fm_elfinder.listener" tag is deprecated, use "kernel.event_listener" instead.', \E_USER_DEPRECATED); + @trigger_error('Using "fm_elfinder.listener" tag is deprecated, use "kernel.event_listener" instead.', E_USER_DEPRECATED); } foreach ($subscribers as $serviceId => $tags) { - @\trigger_error('Using "fm_elfinder.subscriber" tag is deprecated, use "kernel.event_subscriber" instead.', \E_USER_DEPRECATED); + @trigger_error('Using "fm_elfinder.subscriber" tag is deprecated, use "kernel.event_subscriber" instead.', E_USER_DEPRECATED); } - if (\count($listeners) > 0 || \count($subscribers) > 0) { + if (count($listeners) > 0 || count($subscribers) > 0) { $pass = new RegisterListenersPass('event_dispatcher', 'fm_elfinder.listener', 'fm_elfinder.subscriber'); $pass->process($container); } diff --git a/src/DependencyInjection/Compiler/TwigFormPass.php b/src/DependencyInjection/Compiler/TwigFormPass.php index cf603b2..731f186 100644 --- a/src/DependencyInjection/Compiler/TwigFormPass.php +++ b/src/DependencyInjection/Compiler/TwigFormPass.php @@ -2,8 +2,8 @@ namespace FM\ElfinderBundle\DependencyInjection\Compiler; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; class TwigFormPass implements CompilerPassInterface { diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php index 9987ae9..c384fd2 100644 --- a/src/DependencyInjection/Configuration.php +++ b/src/DependencyInjection/Configuration.php @@ -6,6 +6,8 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; +use function method_exists; + final class Configuration implements ConfigurationInterface { /** @@ -486,7 +488,8 @@ private function createDriverOptionsNode() private function createNode($name) { $treeBuilder = new TreeBuilder($name); - if (\method_exists($treeBuilder, 'getRootNode')) { + + if (method_exists($treeBuilder, 'getRootNode')) { $rootNode = $treeBuilder->getRootNode(); } else { $rootNode = $treeBuilder->root($name); diff --git a/src/DependencyInjection/FMElfinderExtension.php b/src/DependencyInjection/FMElfinderExtension.php index 4db100f..163a44d 100644 --- a/src/DependencyInjection/FMElfinderExtension.php +++ b/src/DependencyInjection/FMElfinderExtension.php @@ -3,8 +3,8 @@ namespace FM\ElfinderBundle\DependencyInjection; use FM\ElfinderBundle\Controller\ElFinderController; -use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader\XmlFileLoader; @@ -17,7 +17,7 @@ public function load(array $configs, ContainerBuilder $container): void { $config = $this->processConfiguration(new Configuration(), $configs); - $loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../../config')); + $loader = new XmlFileLoader($container, new FileLocator(__DIR__ . '/../../config')); $loader->load('elfinder.xml'); $loader->load('form.xml'); $loader->load('command.xml'); diff --git a/src/ElFinder/ElFinder.php b/src/ElFinder/ElFinder.php index 74a9e4d..f61dba1 100644 --- a/src/ElFinder/ElFinder.php +++ b/src/ElFinder/ElFinder.php @@ -3,6 +3,7 @@ namespace FM\ElfinderBundle\ElFinder; use elFinder as BaseElFinder; +use elFinderSession; use elFinderSessionInterface; /** @@ -14,8 +15,6 @@ class ElFinder extends BaseElFinder * Constructor. * * @param array elFinder and roots configurations - * - * @author Dmitry (dio) Levashov */ public function __construct($opts) { @@ -46,6 +45,7 @@ public function __construct($opts) // set error handler of WARNING, NOTICE $errLevel = E_WARNING | E_NOTICE | E_USER_WARNING | E_USER_NOTICE | E_STRICT | E_RECOVERABLE_ERROR; + if (defined('E_DEPRECATED')) { $errLevel |= E_DEPRECATED | E_USER_DEPRECATED; } @@ -59,8 +59,10 @@ public function __construct($opts) // convert PATH_INFO to GET query if (!empty($_SERVER['PATH_INFO'])) { $_ps = explode('/', trim($_SERVER['PATH_INFO'], '/')); + if (!isset($_GET['cmd'])) { $_cmd = $_ps[0]; + if (isset($this->commands[$_cmd])) { $_GET['cmd'] = $_cmd; $_i = 1; @@ -82,6 +84,7 @@ public function __construct($opts) // setup debug mode $this->debug = (isset($opts['debug']) && $opts['debug'] ? true : false); + if ($this->debug) { error_reporting(defined('ELFINDER_DEBUG_ERRORLEVEL') ? ELFINDER_DEBUG_ERRORLEVEL : -1); ini_set('diaplay_errors', '1'); @@ -91,7 +94,7 @@ public function __construct($opts) } if (!interface_exists('elFinderSessionInterface')) { - include_once dirname(__FILE__).'/elFinderSessionInterface.php'; + include_once dirname(__FILE__) . '/elFinderSessionInterface.php'; } // session handler @@ -105,12 +108,13 @@ public function __construct($opts) 'netvolume' => !empty($opts['netVolumesSessionKey']) ? $opts['netVolumesSessionKey'] : 'elFinderNetVolumes', ], ]; - $this->session = new \elFinderSession($sessionOpts); + $this->session = new elFinderSession($sessionOpts); } // try session start | restart $this->session->start(); $sessionUseCmds = []; + if (isset($opts['sessionUseCmds']) && is_array($opts['sessionUseCmds'])) { $sessionUseCmds = $opts['sessionUseCmds']; } @@ -123,17 +127,20 @@ public function __construct($opts) $this->time = $this->utime(); $this->sessionCloseEarlier = isset($opts['sessionCloseEarlier']) ? (bool) $opts['sessionCloseEarlier'] : true; $this->sessionUseCmds = array_flip($sessionUseCmds); - $this->timeout = (isset($opts['timeout']) ? $opts['timeout'] : 0); - $this->uploadTempPath = (isset($opts['uploadTempPath']) ? $opts['uploadTempPath'] : ''); - $this->callbackWindowURL = (isset($opts['callbackWindowURL']) ? $opts['callbackWindowURL'] : ''); + $this->timeout = ($opts['timeout'] ?? 0); + $this->uploadTempPath = ($opts['uploadTempPath'] ?? ''); + $this->callbackWindowURL = ($opts['callbackWindowURL'] ?? ''); $this->maxTargets = (isset($opts['maxTargets']) ? intval($opts['maxTargets']) : $this->maxTargets); - self::$commonTempPath = (isset($opts['commonTempPath']) ? $opts['commonTempPath'] : './.tmp'); + self::$commonTempPath = ($opts['commonTempPath'] ?? './.tmp'); + if (!is_writable(self::$commonTempPath)) { self::$commonTempPath = sys_get_temp_dir(); + if (!is_writable(self::$commonTempPath)) { self::$commonTempPath = ''; } } + if (isset($opts['connectionFlagsPath']) && is_writable($opts['connectionFlagsPath'])) { self::$connectionFlagsPath = $opts['connectionFlagsPath']; } else { @@ -143,17 +150,21 @@ public function __construct($opts) if (!empty($opts['tmpLinkPath'])) { self::$tmpLinkPath = $opts['tmpLinkPath']; } + if (!empty($opts['tmpLinkUrl'])) { self::$tmpLinkUrl = $opts['tmpLinkUrl']; } + if (!empty($opts['tmpLinkLifeTime'])) { self::$tmpLinkLifeTime = $opts['tmpLinkLifeTime']; } + if (!empty($opts['textMimes']) && is_array($opts['textMimes'])) { self::$textMimes = $opts['textMimes']; } $this->maxArcFilesSize = isset($opts['maxArcFilesSize']) ? intval($opts['maxArcFilesSize']) : 0; $this->optionsNetVolumes = (isset($opts['optionsNetVolumes']) && is_array($opts['optionsNetVolumes'])) ? $opts['optionsNetVolumes'] : []; + if (isset($opts['itemLockExpire'])) { $this->itemLockExpire = intval($opts['itemLockExpire']); } @@ -164,28 +175,32 @@ public function __construct($opts) // check session cache $_optsMD5 = md5(json_encode($opts['roots'])); + if ($this->session->get('_optsMD5') !== $_optsMD5) { $this->session->set('_optsMD5', $_optsMD5); } // setlocale and global locale regists to elFinder::locale self::$locale = !empty($opts['locale']) ? $opts['locale'] : ('WIN' === substr(PHP_OS, 0, 3) ? 'C' : 'en_US.UTF-8'); + if (false === setlocale(LC_ALL, self::$locale)) { self::$locale = setlocale(LC_ALL, '0'); } // set defaultMimefile - self::$defaultMimefile = (isset($opts['defaultMimefile']) ? $opts['defaultMimefile'] : ''); + self::$defaultMimefile = ($opts['defaultMimefile'] ?? ''); // bind events listeners if (!empty($opts['bind']) && is_array($opts['bind'])) { $_req = 'POST' == $_SERVER['REQUEST_METHOD'] ? $_POST : $_GET; - $_reqCmd = isset($_req['cmd']) ? $_req['cmd'] : ''; + $_reqCmd = $_req['cmd'] ?? ''; foreach ($opts['bind'] as $cmd => $handlers) { $doRegist = (false !== strpos($cmd, '*')); + if (!$doRegist) { $doRegist = ($_reqCmd && in_array($_reqCmd, array_map('self::getCmdOfBind', explode(' ', $cmd)))); } + if ($doRegist) { // for backward compatibility if (!is_array($handlers)) { @@ -198,10 +213,11 @@ public function __construct($opts) foreach ($handlers as $handler) { if ($handler) { if (is_string($handler) && strpos($handler, '.')) { - list($_domain, $_name, $_method) = array_pad(explode('.', $handler), 3, ''); + [$_domain, $_name, $_method] = array_pad(explode('.', $handler), 3, ''); + if (0 === strcasecmp($_domain, 'plugin')) { - if ($plugin = $this->getPluginInstance($_name, isset($opts['plugin'][$_name]) ? $opts['plugin'][$_name] : []) - and method_exists($plugin, $_method)) { + if ($plugin = $this->getPluginInstance($_name, $opts['plugin'][$_name] ?? []) and + method_exists($plugin, $_method)) { $this->bind($cmd, [$plugin, $_method]); } } @@ -224,7 +240,7 @@ public function __construct($opts) if (!isset($root['id'])) { // given fixed unique id if (!$root['id'] = $this->getNetVolumeUniqueId($netVolumes)) { - $this->mountErrors[] = 'Netmount Driver "'.$root['driver'].'" : Could\'t given volume id.'; + $this->mountErrors[] = 'Netmount Driver "' . $root['driver'] . '" : Could\'t given volume id.'; continue; } @@ -252,7 +268,7 @@ public function __construct($opts) protected function mountVolumes($opts) { foreach ($opts['roots'] as $i => $o) { - $class = 'elFinderVolume'.(isset($o['driver']) ? $o['driver'] : ''); + $class = 'elFinderVolume' . ($o['driver'] ?? ''); if (class_exists($class)) { $volume = new $class(); @@ -263,25 +279,27 @@ protected function mountVolumes($opts) } // pass session handler $volume->setSession($this->session); + if ($volume->mount($o)) { // unique volume id (ends on "_") - used as prefix to files hash $id = $volume->id(); $this->volumes[$id] = $volume; + if ((!$this->default || $volume->root() !== $volume->defaultPath()) && $volume->isReadable()) { $this->default = $this->volumes[$id]; } } else { $this->removeNetVolume($i, $volume); - $this->mountErrors[] = 'Driver "'.$class.'" : '.implode(' ', $volume->error()); + $this->mountErrors[] = 'Driver "' . $class . '" : ' . implode(' ', $volume->error()); } } catch (Exception $e) { $this->removeNetVolume($i, $volume); - $this->mountErrors[] = 'Driver "'.$class.'" : '.$e->getMessage(); + $this->mountErrors[] = 'Driver "' . $class . '" : ' . $e->getMessage(); } } else { $this->removeNetVolume($i, $volume); - $this->mountErrors[] = 'Driver "'.$class.'" does not exist'; + $this->mountErrors[] = 'Driver "' . $class . '" does not exist'; } } } diff --git a/src/Event/ElFinderPreExecutionEvent.php b/src/Event/ElFinderPreExecutionEvent.php index dcdd052..93a18f5 100644 --- a/src/Event/ElFinderPreExecutionEvent.php +++ b/src/Event/ElFinderPreExecutionEvent.php @@ -15,13 +15,6 @@ class ElFinderPreExecutionEvent extends Event */ protected $request; - /** - * Used to make sub requests. - * - * @var HttpKernelInterface - */ - private $httpKernel; - /** * ElFinder instance. * @@ -36,6 +29,13 @@ class ElFinderPreExecutionEvent extends Event */ protected $homeFolder; + /** + * Used to make sub requests. + * + * @var HttpKernelInterface + */ + private $httpKernel; + /** * Constructor. * diff --git a/src/Form/Type/ElFinderType.php b/src/Form/Type/ElFinderType.php index 2ab55f9..8df3d35 100644 --- a/src/Form/Type/ElFinderType.php +++ b/src/Form/Type/ElFinderType.php @@ -3,9 +3,9 @@ namespace FM\ElfinderBundle\Form\Type; use Symfony\Component\Form\AbstractType; -use Symfony\Component\Form\FormView; -use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\FormBuilderInterface; +use Symfony\Component\Form\FormInterface; +use Symfony\Component\Form\FormView; use Symfony\Component\OptionsResolver\OptionsResolver; class ElFinderType extends AbstractType @@ -43,9 +43,9 @@ public function configureOptions(OptionsResolver $resolver): void { $resolver ->setDefaults([ - 'enable' => true, - 'instance' => 'default', - 'homeFolder' => '', + 'enable' => true, + 'instance' => 'default', + 'homeFolder' => '', ]) ->setAllowedTypes('enable', 'bool') ->setAllowedTypes('instance', ['string', 'null']) diff --git a/src/Loader/ElFinderLoader.php b/src/Loader/ElFinderLoader.php index 79f96ff..9290f64 100644 --- a/src/Loader/ElFinderLoader.php +++ b/src/Loader/ElFinderLoader.php @@ -2,9 +2,10 @@ namespace FM\ElfinderBundle\Loader; -use FM\ElfinderBundle\Connector\ElFinderConnector; +use Exception; use FM\ElfinderBundle\Bridge\ElFinderBridge; use FM\ElfinderBundle\Configuration\ElFinderConfigurationProviderInterface; +use FM\ElfinderBundle\Connector\ElFinderConnector; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\SessionInterface; @@ -34,15 +35,16 @@ public function __construct(ElFinderConfigurationProviderInterface $configurator } /** - * @throws \Exception + * @throws Exception * * @return array */ public function configure() { $configurator = $this->configurator; + if (!($configurator instanceof ElFinderConfigurationProviderInterface)) { - throw new \Exception('Configurator class must implement ElFinderConfigurationProviderInterface'); + throw new Exception('Configurator class must implement ElFinderConfigurationProviderInterface'); } return $configurator->getConfiguration($this->instance); @@ -53,7 +55,7 @@ public function configure() * * @var string * - * @throws \Exception + * @throws Exception */ public function initBridge($instance, array $efParameters) { @@ -76,6 +78,7 @@ public function initBridge($instance, array $efParameters) } $this->bridge = new ElFinderBridge($this->config); + if ($this->session) { $this->bridge->setSession($this->session); } @@ -94,9 +97,9 @@ public function load(Request $request) if ($this->config['corsSupport']) { return $connector->execute($request->query->all()); - } else { - return $connector->run($request->query->all()); } + + return $connector->run($request->query->all()); } /** @@ -117,7 +120,6 @@ public function setConfigurator(ElFinderConfigurationProviderInterface $configur * * @param string $path * - * @return mixed **/ public function encode($path) { @@ -133,9 +135,9 @@ public function encode($path) return array_values($aPathEncoded)[0]; } elseif (count($aPathEncoded) > 1) { return $aPathEncoded; - } else { - return false; } + + return false; } /** diff --git a/src/Session/ElFinderSession.php b/src/Session/ElFinderSession.php index 6532977..f7d4a37 100644 --- a/src/Session/ElFinderSession.php +++ b/src/Session/ElFinderSession.php @@ -2,9 +2,10 @@ namespace FM\ElfinderBundle\Session; +use elFinderSessionInterface; use Symfony\Component\HttpFoundation\Session\SessionInterface; -class ElFinderSession implements \elFinderSessionInterface +class ElFinderSession implements elFinderSessionInterface { /** @var SessionInterface */ protected $session; diff --git a/src/Twig/Extension/FMElfinderExtension.php b/src/Twig/Extension/FMElfinderExtension.php index 9b3ada7..6b2da26 100644 --- a/src/Twig/Extension/FMElfinderExtension.php +++ b/src/Twig/Extension/FMElfinderExtension.php @@ -36,8 +36,6 @@ public function getFunctions() } /** - * @return mixed - * * @throws LoaderError * @throws RuntimeError * @throws SyntaxError diff --git a/tests/FMElfinderBundleTest.php b/tests/FMElfinderBundleTest.php index b1d9124..afd547e 100644 --- a/tests/FMElfinderBundleTest.php +++ b/tests/FMElfinderBundleTest.php @@ -14,14 +14,4 @@ public function testBundle(): void $this->assertInstanceOf(Bundle::class, $bundle); } - public function testCompilerPasses() - { - $containerBuilder = new ContainerBuilder(); - - $bundle = new FMElfinderBundle(); - $bundle->build($containerBuilder); - - $passes = $containerBuilder->getCompilerPassConfig()->getBeforeOptimizationPasses(); - self::assertEquals(9, count($passes)); - } }