diff --git a/.check-author.yml b/.check-author.yml deleted file mode 100644 index 5fc381c..0000000 --- a/.check-author.yml +++ /dev/null @@ -1,12 +0,0 @@ -ignore: - - 'Mini Model ' - -exclude: - - /^src\/Resources\/contao\/languages/ - -mapping: - 'Andreas Isaak ': 'Andreas Isaak ' - 'Christopher Boelter ': - - 'cogizz ' - - 'cboelter ' - - 'Christopher Boelter ' diff --git a/.composer-require-checker.json b/.composer-require-checker.json new file mode 100644 index 0000000..1fbb404 --- /dev/null +++ b/.composer-require-checker.json @@ -0,0 +1,17 @@ +{ + "symbol-whitelist": [ + "array", "bool", "false", "int", "null", "self", "static", "parent", "string", "true", "void", "mixed", + "Contao\\ManagerBundle\\ContaoManagerBundle", + "Contao\\ManagerPlugin\\Bundle\\BundlePluginInterface", + "Contao\\ManagerPlugin\\Bundle\\Config\\BundleConfig", + "Contao\\ManagerPlugin\\Bundle\\Parser\\ParserInterface", + "Contao\\ManagerPlugin\\Routing\\RoutingPluginInterface", + "MetaModels\\ContaoFrontendEditingBundle\\MetaModelsContaoFrontendEditingBundle", + "InspiredMinds\\ContaoFileUsage\\ContaoFileUsageBundle", + "InspiredMinds\\ContaoFileUsage\\Provider\\FileUsageProviderInterface", + "InspiredMinds\\ContaoFileUsage\\Result\\FileTreeMultipleResult", + "InspiredMinds\\ContaoFileUsage\\Result\\FileTreeSingleResult", + "InspiredMinds\\ContaoFileUsage\\Result\\ResultInterface", + "InspiredMinds\\ContaoFileUsage\\Result\\ResultsCollection" + ] +} diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..273e702 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,9 @@ +version: 2 +updates: +- package-ecosystem: composer + directory: "/" + schedule: + interval: daily + time: "04:00" + timezone: Europe/Berlin + open-pull-requests-limit: 10 diff --git a/.github/workflows/diagnostics.yml b/.github/workflows/diagnostics.yml new file mode 100644 index 0000000..9d4510b --- /dev/null +++ b/.github/workflows/diagnostics.yml @@ -0,0 +1,75 @@ +name: MetaModels attribute_file + +on: + pull_request: + push: + branches: + +jobs: + build: + runs-on: ubuntu-latest + name: 'PHP: ${{ matrix.php }} Contao: ${{ matrix.contao }}' + strategy: + fail-fast: false + matrix: + php: [ '8.1', '8.2', '8.3' ] + contao: [ '~4.13.0' ] + phpcq_install: [ 'update' ] + output: [ '-o github-action -o default' ] + + steps: + - name: Pull source + uses: actions/checkout@v4 + + - name: Setup PHP with PECL extension + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + + # setup caches + - name: Cache composer cache directory + uses: actions/cache@v4 + env: + cache-name: composer-cache-dir + with: + path: ~/.cache/composer + key: ${{ runner.os }}-${{ matrix.php }}-build-${{ env.cache-name }} + + - name: Cache vendor directory + uses: actions/cache@v4 + env: + cache-name: vendor + with: + path: vendor + key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.contao }}-build-${{ env.cache-name }}-${{ hashFiles('**/composer.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.php }}-${{ matrix.contao }}-build-${{ env.cache-name }}- + + - name: Cache phpcq directory + uses: actions/cache@v4 + env: + cache-name: phpcq + with: + path: .phpcq + key: ${{ runner.os }}-${{ matrix.php }}-build-${{ env.cache-name }}-${{ hashFiles('**/.phpcq.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.php }}-build-${{ env.cache-name }}- + + # install dependencies and tools + - name: Install composer dependencies + run: | + composer require contao/core-bundle ${{ matrix.contao }} --no-update + composer install + - name: Install phpcq toolchain + run: ./vendor/bin/phpcq ${{ matrix.phpcq_install }} -v + + # run tests + - name: Run tests + run: ./vendor/bin/phpcq run -v ${{ matrix.output }} + + - name: Upload build directory to artifact + uses: actions/upload-artifact@v4 + if: ${{ success() }} || ${{ failure() }} + with: + name: phpcq-builds-php-${{ matrix.php }}-${{ matrix.contao }} + path: .phpcq/build/ diff --git a/.gitignore b/.gitignore index 3ef8d6a..eccdc19 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,7 @@ Thumbs.db .build/ .external*/ .idea/ +.phpcq/ nbproject/ # composer related @@ -17,3 +18,4 @@ vendor/ # build build/ /composer.lock +.phpunit.result.cache diff --git a/.phpcq.lock b/.phpcq.lock new file mode 100644 index 0000000..f19c1f3 --- /dev/null +++ b/.phpcq.lock @@ -0,0 +1 @@ +{"plugins":{"phpunit":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpunit/phpunit-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0"},"tool":{"phpunit":"^6.0 || ^7.0 || ^8.0 || ^9.0"}},"checksum":{"type":"sha-512","value":"c73f15658e3ba62665f09492ec91c3a6a715760bfaa88473a987538439fff442540148e086e46a6aa18ce55a3ea2fbf76caaa581384cb84a38859fcc609ae7e4"},"tools":{"phpunit":{"version":"9.6.22","url":"https://phar.phpunit.de/phpunit-9.6.22.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-xml":"*","ext-xmlwriter":"*"}},"checksum":{"type":"sha-256","value":"9618d52015c9b06b4979a8e481ca9567be6be20e711e98926c61378a400e1f2e"},"signature":"https://phar.phpunit.de/phpunit-9.6.22.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.3.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.3.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0 || ^6.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"5.26.1","url":"https://github.com/vimeo/psalm/releases/download/5.26.1/psalm.phar","requirements":{"php":{"php":"^7.4 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0","ext-SimpleXML":"*","ext-ctype":"*","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-tokenizer":"*"}},"checksum":null,"signature":"https://github.com/vimeo/psalm/releases/download/5.26.1/psalm.phar.asc"}},"composerLock":null},"composer-require-checker":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-require-checker/composer-require-checker-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0"},"tool":{"composer-require-checker":"^3.8 || ^4.0"}},"checksum":{"type":"sha-512","value":"d5415bddfe024c5749d894034583882aee4e5c3e1087815d9fdd81cb5e71630f631a0e35de0ff84b97fbbf738c16ece5f83bd8c00695913eb846aa6f04577dc2"},"tools":{"composer-require-checker":{"version":"4.7.1","url":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.7.1/composer-require-checker.phar","requirements":{"php":{"php":"~8.1.0 || ~8.2.0 || ~8.3.0","ext-phar":"*"}},"checksum":null,"signature":"https://github.com/maglnet/ComposerRequireChecker/releases/download/4.7.1/composer-require-checker.phar.asc"}},"composerLock":null},"phpmd":{"api-version":"1.0.0","version":"1.0.2.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpmd/phpmd-1.0.2.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpmd":"^2.6.1"}},"checksum":{"type":"sha-512","value":"f22280a6dec8dbdd2ec1d83b294f23237fe32c34f4a298e52038e0a7a0074d541635b2b488b1a6098a42d8418a6cd8eb804406ea82b91e362be2b5d11a0915b0"},"tools":{"phpmd":{"version":"2.15.0","url":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar","requirements":{"php":{"php":">=5.3.9","ext-xml":"*"}},"checksum":null,"signature":"https://github.com/phpmd/phpmd/releases/download/2.15.0/phpmd.phar.asc"}},"composerLock":null},"phpcpd":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcpd/phpcpd-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcpd":"^6.0"}},"checksum":{"type":"sha-512","value":"1189ce0bf3fade4cb4241f1d96f915ef8fc7651f4450dc79fdf464ee3d6be3009316f0d423ce2d4af9d76ad50807b7fdf4d77bfa6d9ee2c91d6eda32ea214433"},"tools":{"phpcpd":{"version":"6.0.3","url":"https://phar.phpunit.de/phpcpd-6.0.3.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*"}},"checksum":{"type":"sha-256","value":"2cbaea7cfda1bb4299d863eb075e977c3f49055dd16d88529fae5150d48a84cb"},"signature":"https://phar.phpunit.de/phpcpd-6.0.3.phar.asc"}},"composerLock":null},"phploc":{"api-version":"1.0.0","version":"1.0.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phploc/phploc-1.0.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*","ext-json":"*"},"tool":{"phploc":"^3.0 || ^4.0 || ^5.0 || ^6.0 || ^7.0"}},"checksum":{"type":"sha-512","value":"f67b02d494796adf553cb3dd13ec06c1cb8e53c799954061749424251379541637538199afb3afa3c7a01cabd1cb6f1c53eb621f015dff9644c6c7cbf10c56d1"},"tools":{"phploc":{"version":"7.0.2","url":"https://phar.phpunit.de/phploc-7.0.2.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*"}},"checksum":{"type":"sha-256","value":"3d59778ec86faf25fd00e3a329b2f9ad4a3c751ca91601ea7dab70f887b0bf46"},"signature":"https://phar.phpunit.de/phploc-7.0.2.phar.asc"}},"composerLock":null},"phpcs":{"api-version":"1.0.0","version":"1.2.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/phpcs/phpcs-1.2.0.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-dom":"*"},"tool":{"phpcs":"^3.0 || ^2.0","phpcbf":"^3.0 || ^2.0"}},"checksum":{"type":"sha-512","value":"b6ed00306e76068a6af5e3b1dec837724f9e1900ef1049ce88e7ce195b0583524ca33a73613fba13244307a7ca853b6ddaa14ded69f651c3f184ac130bd1aaad"},"tools":{"phpcs":{"version":"3.12.0","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/3.12.0/phpcs.phar","requirements":{"php":{"php":">=5.4.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/3.12.0/phpcs.phar.asc"},"phpcbf":{"version":"3.12.0","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/3.12.0/phpcbf.phar","requirements":{"php":{"php":">=5.4.0","ext-simplexml":"*","ext-tokenizer":"*","ext-xmlwriter":"*"}},"checksum":null,"signature":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/3.12.0/phpcbf.phar.asc"}},"composerLock":null},"composer-normalize":{"api-version":"1.0.0","version":"1.1.1.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/composer-normalize/composer-normalize-1.1.1.0.php","signature":null,"requirements":{"php":{"php":"^7.3 || ^8.0","ext-json":"*"},"tool":{"composer-normalize":"^2.1"}},"checksum":{"type":"sha-512","value":"d9abda440b85d501c58abf9c81bf76f417594b397129215ffa8b777e9bb5e5eda37d7661d661db3c8d11c24f20345bc6fbe56f013b3b9435d459d2b94f086e0f"},"tools":{"composer-normalize":{"version":"2.45.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.45.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.45.0/composer-normalize.phar.asc"}},"composerLock":null}},"tools":[]} \ No newline at end of file diff --git a/.phpcq.yaml.dist b/.phpcq.yaml.dist new file mode 100644 index 0000000..a0ff982 --- /dev/null +++ b/.phpcq.yaml.dist @@ -0,0 +1,117 @@ +phpcq: + repositories: + - https://phpcq.github.io/repository/repository.json + directories: + - src + - tests + artifact: .phpcq/build + composer: + autodiscover: false + + plugins: + phpunit: + version: ^1.0 + signed: false + psalm: + version: ^1.0 + signed: false + requirements: + psalm: + version: ^5.26.1 + composer-require-checker: + version: ^1.0 + signed: false + phpmd: + version: ^1.0 + signed: false + phpcpd: + version: ^1.1 + signed: false + phploc: + version: ^1.0 + signed: false + phpcs: + version: ^1.0 + signed: false + composer-normalize: + version: ^1.0 + signed: false + trusted-keys: + # composer-require-checker + - 033E5F8D801A2F8D + # sb@sebastian-bergmann.de + - 4AA394086372C20A + # psalm + - 8A03EA3B385DBAA1 + - 12CE0F1D262429A5 + # magl@magll.net + - D2CCAC42F6295E7D + # PHP_CodeSniffer + - 31C7E470E2138192 + - 5E6DDE998AB73B8E + - A978220305CD5C32 + # Composer normalize + - C00543248C87FB13 + # phpmd + - A4E55EA12C7C085C + - 9093F8B32E4815AA + +tasks: + fix: + - composer-normalize-fix + - phpcbf + + verify: + - composer-require-checker + - composer-normalize + + analyze: + - phploc + - phpcpd + - phpmd + - phpcs + - psalm + - phpunit + + default: + - verify + - analyze + + phpcpd: + plugin: phpcpd + config: + exclude: + - tests + - src/EventListener/DcGeneral/Table/DcaSetting/FeeFileImageSizeOptions.php + - src/EventListener/DcGeneral/Table/RenderSetting/FileImageSizeOptions.php + + phpmd: + plugin: phpmd + config: + ruleset: + - ./.phpmd.xml + + composer-require-checker: + plugin: composer-require-checker + config: + config_file: '.composer-require-checker.json' + + phpcs: + plugin: phpcs + config: &phpcs-config + standard: PSR12 + excluded: + - '*/Resources/contao/dca/*' + - '*/Resources/contao/languages/*' + - '*/Resources/public/*' + + phpcbf: + plugin: phpcs + config: + <<: *phpcs-config + fix: true + + composer-normalize-fix: + plugin: composer-normalize + config: + dry_run: false diff --git a/.phpmd.xml b/.phpmd.xml new file mode 100644 index 0000000..78eb716 --- /dev/null +++ b/.phpmd.xml @@ -0,0 +1,37 @@ + + + + PHPMD rule set + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 4bc4168..0000000 --- a/.travis.yml +++ /dev/null @@ -1,63 +0,0 @@ -dist: xenial - -addons: - apt: - packages: - - ant-optional - -language: php - -php: - - "7.3" - - "7.2" - - "7.1" - -env: - - CONTAO_VERSION=~4.7.0 - - CONTAO_VERSION=~4.6.0 - - CONTAO_VERSION=~4.5.0 - - CONTAO_VERSION=~4.4.0 - -# Exclude impossible Contao Version combinations. -matrix: - exclude: - fast_finish: true - allow_failures: - - env: CONTAO_VERSION=~4.7.0 - - env: CONTAO_VERSION=~4.6.0 - - env: CONTAO_VERSION=~4.5.0 - -before_script: - - echo "memory_limit=-1" >> ~/.phpenv/versions/$(phpenv version-name)/etc/conf.d/travis.ini - - travis_retry composer self-update && composer --version - - travis_retry composer require contao/core-bundle $CONTAO_VERSION --no-update - - > - if [ "x${TRAVIS_TAG}" != "x" ]; then - export COMPOSER_ROOT_VERSION=${TRAVIS_TAG} - else - export COMPOSER_ROOT_VERSION=$([[ ${TRAVIS_BRANCH} =~ (hotfix|release)/([0-9.]*(-(alpha|beta|rc)[0-9]+)?) ]] \ - && echo ${BASH_REMATCH[2]} \ - || echo dev-${TRAVIS_BRANCH}) - fi - - echo "Using root version ${COMPOSER_ROOT_VERSION}" - - > - echo "PHP version: ${TRAVIS_PHP_VERSION}"; - if [ "x${TRAVIS_PHP_VERSION}" == "xnightly" ]; then - travis_retry composer update --ignore-platform-reqs --prefer-dist --no-interaction --no-suggest - else - travis_retry composer update --prefer-dist --no-interaction --no-suggest - fi - -script: ant -keep-going - -# Hack to make things work again - we can not use a shallow repository. -git: - depth: 2147483647 - -branches: - except: - - /.*-translation/ - -cache: - directories: - - vendor diff --git a/.tx/config b/.tx/config new file mode 100644 index 0000000..6a1f87a --- /dev/null +++ b/.tx/config @@ -0,0 +1,29 @@ +[main] +host = https://app.transifex.com + +[o:metamodels:p:attribute-file:r:tl_metamodel_attribute] +file_filter = src/Resources/translations/tl_metamodel_attribute..xlf +source_file = src/Resources/translations/tl_metamodel_attribute.en.xlf +type = XLIFF +minimum_perc = 0 +resource_name = tl_metamodel_attribute +replace_edited_strings = false +keep_translations = false + +[o:metamodels:p:attribute-file:r:tl_metamodel_dcasetting] +file_filter = src/Resources/translations/tl_metamodel_dcasetting..xlf +source_file = src/Resources/translations/tl_metamodel_dcasetting.en.xlf +type = XLIFF +minimum_perc = 0 +resource_name = tl_metamodel_dcasetting +replace_edited_strings = false +keep_translations = false + +[o:metamodels:p:attribute-file:r:tl_metamodel_rendersetting] +file_filter = src/Resources/translations/tl_metamodel_rendersetting..xlf +source_file = src/Resources/translations/tl_metamodel_rendersetting.en.xlf +type = XLIFF +minimum_perc = 0 +resource_name = tl_metamodel_rendersetting +replace_edited_strings = false +keep_translations = false diff --git a/README.md b/README.md index ef76528..554eebe 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -[![Build Status](https://travis-ci.org/MetaModels/attribute_file.svg)](https://travis-ci.org/MetaModels/attribute_file/branches) +[![Build Status](https://github.com/MetaModels/attribute_file/actions/workflows/diagnostics.yml/badge.svg)](https://github.com/MetaModels/attribute_file/actions) [![Latest Version tagged](http://img.shields.io/github/tag/MetaModels/attribute_file.svg)](https://github.com/MetaModels/attribute_file/tags) [![Latest Version on Packagist](http://img.shields.io/packagist/v/MetaModels/attribute_file.svg)](https://packagist.org/packages/MetaModels/attribute_file) [![Installations via composer per month](http://img.shields.io/packagist/dm/MetaModels/attribute_file.svg)](https://packagist.org/packages/MetaModels/attribute_file) @@ -6,4 +6,4 @@ File ==== -The file attribute +The file attribute. diff --git a/build.default.properties b/build.default.properties deleted file mode 100644 index c81a16f..0000000 --- a/build.default.properties +++ /dev/null @@ -1,11 +0,0 @@ -##################################################### -## This project is using the ## -## PHP code quality project (phpcq) ## -## ## -## https://github.com/phpcq/phpcq ## -##################################################### - -phpcs.standard=${basedir}/vendor/phpcq/coding-standard/phpcs/PhpCodeQuality/ruleset.xml -phpmd.ruleset=${basedir}/vendor/phpcq/coding-standard/phpmd/ruleset.xml - -phpcs.excluded=src/Resources/contao/languages diff --git a/build.xml b/build.xml deleted file mode 100644 index 2b34341..0000000 --- a/build.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/composer.json b/composer.json index 06459b1..ec2aa94 100644 --- a/composer.json +++ b/composer.json @@ -1,6 +1,8 @@ { "name": "metamodels/attribute_file", "description": "MetaModels attribute for files", + "license": "LGPL-3.0-or-later", + "type": "contao-bundle", "keywords": [ "contao", "metamodels", @@ -8,14 +10,11 @@ "file", "nontranslatable" ], - "type": "contao-bundle", - "homepage": "http://now.metamodel.me/", - "license": "LGPL-3.0-or-later", "authors": [ { "name": "Christian Schiffler", "email": "c.schiffler@cyberspectrum.de", - "homepage": "http://www.cyberspectrum.de/", + "homepage": "https://www.cyberspectrum.de/", "role": "Developer" }, { @@ -24,26 +23,34 @@ "role": "Developer" } ], + "homepage": "https://now.metamodel.me/", "support": { "email": "mail@metamodel.me", "issues": "https://github.com/MetaModels/attribute_file/issues", - "wiki": "http://de.contaowiki.org/MetaModels", - "irc": "irc://irc.freenode.org/contao.mm", + "wiki": "https://de.contaowiki.org/MetaModels", "source": "https://github.com/MetaModels/attribute_file" }, "require": { - "php": "^7.1", - "contao-community-alliance/dc-general": "^2.1.3", - "contao/core-bundle": "^4.4.8", - "metamodels/core": "^2.1.1", - "symfony/dependency-injection": "^3.3 || ^4.0", - "symfony/http-kernel": "^3.3 || ^4.0", - "ext-pdo": "*" + "php": "^8.1", + "contao-community-alliance/dc-general": "^2.3", + "contao/core-bundle": "^4.13.0 <5.0", + "doctrine/dbal": "^3.6.0", + "metamodels/core": "^2.3", + "symfony/cache": "^5.4", + "symfony/config": "^5.4", + "symfony/dependency-injection": "^5.4", + "symfony/http-foundation": "^5.4", + "symfony/http-kernel": "^5.4", + "symfony/routing": "^5.4", + "symfony/security-core": "^5.4", + "symfony/translation-contracts": "^2.5.2" }, "require-dev": { "contao/manager-plugin": "^2.1", + "inspiredminds/contao-file-usage": "^3.0.1", "menatwork/contao-multicolumnwizard-bundle": "^3.4", - "phpcq/all-tasks": "^1.2" + "metamodels/contao-frontend-editing": "^2.3", + "phpcq/runner-bootstrap": "^1.0@dev" }, "autoload": { "psr-4": { @@ -58,13 +65,18 @@ "MetaModels\\AttributeFileBundle\\Test\\": "tests" } }, - "extra": { - "contao-manager-plugin": "MetaModels\\AttributeFileBundle\\ContaoManager\\Plugin", - "branch-alias": { - "dev-master": "2.1.x-dev" - } - }, "config": { + "allow-plugins": { + "contao-components/installer": false, + "contao/manager-plugin": false, + "php-http/discovery": true + }, "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-feature/2.3.0": "2.3.x-dev" + }, + "contao-manager-plugin": "MetaModels\\AttributeFileBundle\\ContaoManager\\Plugin" } } diff --git a/phpunit.xml.dist b/phpunit.xml.dist index 39489a9..0752207 100644 --- a/phpunit.xml.dist +++ b/phpunit.xml.dist @@ -1,7 +1,7 @@ ./tests - - + + ./src - - + + diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 0000000..663fb43 --- /dev/null +++ b/psalm.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/src/Attribute/AttributeOrderTypeFactory.php b/src/Attribute/AttributeOrderTypeFactory.php index 06ccb9d..3cb7599 100644 --- a/src/Attribute/AttributeOrderTypeFactory.php +++ b/src/Attribute/AttributeOrderTypeFactory.php @@ -86,14 +86,14 @@ public function getTypeIcon() */ public function createInstance($information, $metaModel) { - $columnName = ($information['colname'] ?? null); + $columnName = (string) ($information['colname'] ?? ''); $tableName = $metaModel->getTableName(); if (!isset($this->tableColumns[$tableName])) { - $this->tableColumns[$tableName] = $this->connection->getSchemaManager()->listTableColumns($tableName); + $this->tableColumns[$tableName] = $this->connection->createSchemaManager()->listTableColumns($tableName); } - if (!$columnName || !\array_key_exists($columnName, $this->tableColumns[$tableName])) { + if (('' === $columnName) || !\array_key_exists($columnName, $this->tableColumns[$tableName])) { return null; } diff --git a/src/Attribute/AttributeTypeFactory.php b/src/Attribute/AttributeTypeFactory.php index 795ec16..4f35ee0 100644 --- a/src/Attribute/AttributeTypeFactory.php +++ b/src/Attribute/AttributeTypeFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Stefan Heimes - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,7 +24,6 @@ use Contao\Config; use Contao\CoreBundle\Framework\Adapter; -use Contao\CoreBundle\Image\ImageFactoryInterface; use Contao\FilesModel; use Contao\StringUtil; use Doctrine\DBAL\Connection; @@ -56,46 +56,46 @@ class AttributeTypeFactory implements IAttributeTypeFactory * * @var ToolboxFile */ - private $toolboxFile; + private ToolboxFile $toolboxFile; /** * The string util. * - * @var Adapter|StringUtil + * @var Adapter */ - private $stringUtil; + private Adapter $stringUtil; /** * The validator. * - * @var Adapter|Validator + * @var Adapter */ - private $validator; + private Adapter $validator; /** * The repository for files. * - * @var Adapter|FilesModel + * @var Adapter */ - private $fileRepository; + private Adapter $fileRepository; /** * The contao configurations. * - * @var Adapter|Config + * @var Adapter */ - private $config; + private Adapter $config; /** * {@inheritDoc} * - * @param Connection $connection The database connection. - * @param TableManipulator $tableManipulator The table manipulator. - * @param ToolboxFile $toolboxFile The toolbox for file. - * @param Adapter|StringUtil $stringUtil The string util. - * @param Adapter|Validator $validator The validator. - * @param Adapter|FilesModel $fileRepository The repository for files. - * @param Adapter|Config $config The contao configurations. + * @param Connection $connection The database connection. + * @param TableManipulator $tableManipulator The table manipulator. + * @param ToolboxFile $toolboxFile The toolbox for file. + * @param Adapter $stringUtil The string util. + * @param Adapter $validator The validator. + * @param Adapter $fileRepository The repository for files. + * @param Adapter $config The contao configurations. */ public function __construct( Connection $connection, diff --git a/src/Attribute/File.php b/src/Attribute/File.php index 4f76b0d..a3bdb6f 100644 --- a/src/Attribute/File.php +++ b/src/Attribute/File.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -22,7 +22,8 @@ * @author Marc Reimann * @author David Molineus * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,89 +36,97 @@ use Contao\StringUtil; use Contao\System; use Contao\Validator; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Platforms\Keywords\KeywordList; use MetaModels\Attribute\BaseComplex; -use MetaModels\AttributeFileBundle\Doctrine\DBAL\Platforms\Keywords\NotSupportedKeywordList; +use MetaModels\Attribute\ManagedAttributeTrait; use MetaModels\Helper\TableManipulator; use MetaModels\Helper\ToolboxFile; use MetaModels\IMetaModel; use MetaModels\Render\Template; +use function array_key_exists; +use function array_map; +use function array_merge; +use function array_values; +use function is_array; +use function serialize; +use function sprintf; +use function str_replace; +use function trigger_error; +use function trim; + /** * This is the MetaModel attribute class for handling file fields. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class File extends BaseComplex { + use ManagedAttributeTrait; + /** * The database connection. * - * @var Connection|null + * @var Connection */ - private $connection; + private Connection $connection; /** * Table manipulator instance. * - * @var TableManipulator|null + * @var TableManipulator */ - private $tableManipulator; + private TableManipulator $tableManipulator; /** * The toolbox for file. * - * @var ToolboxFile|null + * @var ToolboxFile */ - private $toolboxFile; + private ToolboxFile $toolboxFile; /** * The string util. * - * @var Adapter|StringUtil|null + * @var Adapter */ - private $stringUtil; + private Adapter $stringUtil; /** * The validator. * - * @var Adapter|Validator|null + * @var Adapter */ - private $validator; + private Adapter $validator; /** * The repository for files. * - * @var Adapter|FilesModel|null + * @var Adapter */ - private $fileRepository; + private Adapter $fileRepository; /** * The contao configurations. * - * @var Adapter|Config|null - */ - private $config; - - /** - * The platform reserved keyword list. - * - * @var KeywordList + * @var Adapter */ - private $platformReservedWord; + private Adapter $config; /** * Create a new instance. * - * @param IMetaModel $metaModel The MetaModel instance this attribute belongs to. - * @param array $information The attribute information. - * @param Connection|null $connection The database connection. - * @param TableManipulator|null $tableManipulator Table manipulator instance. - * @param ToolboxFile|null $toolboxFile The toolbox for file. - * @param Adapter|StringUtil|null $stringUtil The string util. - * @param Adapter|Validator|null $validator The validator. - * @param Adapter|FilesModel|null $fileRepository The repository for files. - * @param Adapter|Config|null $config The contao configurations. + * @param IMetaModel $metaModel The MetaModel instance this attribute belongs to. + * @param array $information The attribute information. + * @param Connection|null $connection The database connection. + * @param TableManipulator|null $tableManipulator Table manipulator instance. + * @param ToolboxFile|null $toolboxFile The toolbox for file. + * @param Adapter|null $stringUtil The string util. + * @param Adapter|null $validator The validator. + * @param Adapter|null $fileRepository The repository for files. + * @param Adapter|null $config The contao configurations. */ public function __construct( IMetaModel $metaModel, @@ -132,64 +141,71 @@ public function __construct( ) { parent::__construct($metaModel, $information); - if (null === $toolboxFile) { - // @codingStandardsIgnoreStart - @\trigger_error( - '"toolboxFile"" is missing. It has to be passed in the constructor.' . - 'Fallback will get removed in MetaModels 3.0', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd + if (null === $connection) { + $connection = $this->fetchServiceForFallback('connection', 'database_connection'); + assert($connection instanceof Connection); + } + + if (null === $tableManipulator) { + $tableManipulator = $this->fetchServiceForFallback('tableManipulator', 'metamodels.table_manipulator'); + assert($tableManipulator instanceof TableManipulator); + } - $toolboxFile = System::getContainer()->get('metamodels.attribute_file.toolbox.file'); + if (null === $toolboxFile) { + $toolboxFile = $this->fetchServiceForFallback('toolboxFile', 'MetaModels\Helper\ToolboxFile'); + assert($toolboxFile instanceof ToolboxFile); } if (null === $stringUtil) { // @codingStandardsIgnoreStart - @\trigger_error( + @trigger_error( '"stringUtil"" is missing. It has to be passed in the constructor.' . 'Fallback will get removed in MetaModels 3.0', E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $stringUtil = System::getContainer()->get('contao.framework')->getAdapter(StringUtil::class); + $stringUtil = System::getContainer()->get('contao.framework')?->getAdapter(StringUtil::class); + assert($stringUtil instanceof Adapter); } if (null === $validator) { // @codingStandardsIgnoreStart - @\trigger_error( + @trigger_error( '"validator"" is missing. It has to be passed in the constructor.' . 'Fallback will get removed in MetaModels 3.0', E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $validator = System::getContainer()->get('contao.framework')->getAdapter(Validator::class); + $validator = System::getContainer()->get('contao.framework')?->getAdapter(Validator::class); + assert($validator instanceof Adapter); } if (null === $fileRepository) { // @codingStandardsIgnoreStart - @\trigger_error( + @trigger_error( '"fileRepository"" is missing. It has to be passed in the constructor.' . 'Fallback will get removed in MetaModels 3.0', E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $fileRepository = System::getContainer()->get('contao.framework')->getAdapter(FilesModel::class); + $fileRepository = System::getContainer()->get('contao.framework')?->getAdapter(FilesModel::class); + assert($fileRepository instanceof Adapter); } if (null === $config) { // @codingStandardsIgnoreStart - @\trigger_error( + @trigger_error( '"config"" is missing. It has to be passed in the constructor.' . 'Fallback will get removed in MetaModels 3.0', E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $config = System::getContainer()->get('contao.framework')->getAdapter(Config::class); + $config = System::getContainer()->get('contao.framework')?->getAdapter(Config::class); + assert($config instanceof Adapter); } $this->connection = $connection; @@ -206,16 +222,24 @@ public function __construct( */ public function destroyAUX() { + if ($this->isManagedAttribute($this->get('type'))) { + $this->triggerDeprecationShouldNotCallManaged(static::class, __METHOD__); + return; + } + + $this->triggerDeprecationIsUnmanagedAttribute(static::class, __METHOD__); + + /** @psalm-suppress DeprecatedMethod */ parent::destroyAUX(); $metaModel = $this->getMetaModel()->getTableName(); // Try to delete the column. If it does not exist as we can assume it has been deleted already then. - $tableColumns = $this->connection->getSchemaManager()->listTableColumns($metaModel); - if (($colName = $this->getColName()) && \array_key_exists($colName, $tableColumns)) { + $tableColumns = $this->connection->createSchemaManager()->listTableColumns($metaModel); + if (($colName = $this->getColName()) && array_key_exists($colName, $tableColumns)) { $this->tableManipulator->dropColumn($metaModel, $colName); } - if (\array_key_exists($colName . '__sort', $tableColumns)) { + if (array_key_exists($colName . '__sort', $tableColumns)) { $this->tableManipulator->dropColumn($metaModel, $colName . '__sort'); } } @@ -225,31 +249,69 @@ public function destroyAUX() */ public function initializeAUX() { + if ($this->isManagedAttribute($this->get('type'))) { + $this->triggerDeprecationShouldNotCallManaged(static::class, __METHOD__); + return; + } + + $this->triggerDeprecationIsUnmanagedAttribute(static::class, __METHOD__); + + /** @psalm-suppress DeprecatedMethod */ parent::initializeAUX(); if ($colName = $this->getColName()) { - $tableName = $this->quoteReservedWord($this->getMetaModel()->getTableName()); - $this->tableManipulator->createColumn($tableName, $this->quoteReservedWord($colName), 'blob NULL'); + $tableName = $this->getMetaModel()->getTableName(); + $this->tableManipulator->createColumn($tableName, $colName, 'blob NULL'); } } /** + * Search for file names or UUIDs. + * Find items if one or more files are stored (serialised array) or the parent folder has been selected. + * * {@inheritdoc} */ - public function searchFor($strPattern) + public function searchFor($strPattern): ?array { $subSelect = $this->connection->createQueryBuilder(); $subSelect - ->select($this->quoteReservedWord('uuid')) - ->from($this->quoteReservedWord('tl_files')) - ->where($subSelect->expr()->like($this->quoteReservedWord('path'), ':value')); + ->select('f.uuid', 'f.pid') + ->from('tl_files', 'f'); + + if (Validator::isUuid($uuid = trim($strPattern, '*'))) { + $subSelect + ->where(('f.uuid = :value')) + ->setParameter('value', StringUtil::uuidToBin($uuid)); + } else { + $subSelect + ->where($subSelect->expr()->like('f.name', ':value')) + ->setParameter('value', str_replace(['*', '?'], ['%', '_'], $strPattern)); + } + + if ([] === ($subResults = $subSelect->executeQuery()->fetchAllAssociative())) { + return []; + } + $builder = $this->connection->createQueryBuilder(); $builder - ->select($this->quoteReservedWord('id')) - ->from($this->getMetaModel()->getTableName()) - ->where($builder->expr()->in($this->quoteReservedWord($this->getColName()), $subSelect->getSQL())) - ->setParameter('value', \str_replace(['*', '?'], ['%', '_'], $strPattern)); + ->select('t.id') + ->from($this->getMetaModel()->getTableName(), 't'); + + $uuids = []; + foreach ($subResults as $subResult) { + $uuids[$subResult['pid']] = $subResult['pid']; + $uuids[$subResult['uuid']] = $subResult['uuid']; + } + $colName = $this->getColName(); + foreach (array_values($uuids) as $key => $uuid) { + $builder + ->orWhere(sprintf('t.%s LIKE :value_%s', $colName, $key)) + ->setParameter('value_' . $key, '%' . $uuid . '%'); + } + + $statement = $builder->executeQuery(); - return $builder->execute()->fetchAll(\PDO::FETCH_COLUMN); + // Return value list as list, parent function wants a list so we make a cast. + return array_map(static fn(mixed $value) => (string) $value, $statement->fetchFirstColumn()); } /** @@ -259,17 +321,17 @@ public function unsetDataFor($arrIds) { $builder = $this->connection->createQueryBuilder(); $builder - ->update($this->quoteReservedWord($this->getMetaModel()->getTableName())) - ->set($this->quoteReservedWord($this->getColName()), ':null') - ->where($builder->expr()->in($this->quoteReservedWord('id'), ':values')) - ->setParameter('values', $arrIds, Connection::PARAM_STR_ARRAY) - ->setParameter('null', null); + ->update($this->getMetaModel()->getTableName(), 't') + ->set('t.' . $this->getColName(), ':null') + ->where($builder->expr()->in('t.id', ':values')) + ->setParameter('null', null) + ->setParameter('values', $arrIds, ArrayParameterType::STRING); if ($this->getMetaModel()->hasAttribute($this->getColName() . '__sort')) { - $builder->set($this->quoteReservedWord($this->getColName() . '__sort'), ':null'); + $builder->set('t.' . $this->getColName() . '__sort', ':null'); } - $builder->execute(); + $builder->executeQuery(); } /** @@ -279,28 +341,26 @@ public function getDataFor($arrIds) { $builder = $this->connection->createQueryBuilder(); - $idField = $this->quoteReservedWord('id'); - $aliasFile = $this->quoteReservedWord('file'); $builder - ->select($idField, ' ' . $this->quoteReservedWord($this->getColName()) . ' ' . $aliasFile) - ->from($this->getMetaModel()->getTableName()) - ->where($builder->expr()->in($idField, ':values')) - ->setParameter('values', $arrIds, Connection::PARAM_STR_ARRAY); + ->select('t.id, t.' . $this->getColName() . ' AS file') + ->from($this->getMetaModel()->getTableName(), 't') + ->where($builder->expr()->in('t.id', ':values')) + ->setParameter('values', $arrIds, ArrayParameterType::STRING); if ($hasSort = $this->getMetaModel()->hasAttribute($this->getColName() . '__sort')) { - $sortField = $this->quoteReservedWord($this->getColName() . '__sort'); - $aliasFileSort = $this->quoteReservedWord('file_sort'); - $builder->addSelect($sortField . ' ' . $aliasFileSort); + $builder->addSelect($this->getColName() . '__sort AS file_sort'); } - $query = $builder->execute(); - $data = []; - while ($result = $query->fetch(\PDO::FETCH_OBJ)) { - $row = $this->toolboxFile->convertValuesToMetaModels($this->stringUtil->deserialize($result->file, true)); + $query = $builder->executeQuery(); + + $data = []; + while ($result = $query->fetchAssociative()) { + $row = + $this->toolboxFile->convertValuesToMetaModels($this->stringUtil->deserialize($result['file'], true)); if ($hasSort) { // The sort key be can remove in later version. The new sort key is bin_sorted. - $row['sort'] = $sorted = $this->stringUtil->deserialize($result->file_sort, true); + $row['sort'] = $sorted = $this->stringUtil->deserialize($result['file_sort'], true); foreach ($this->toolboxFile->convertValuesToMetaModels($sorted) as $sortedKey => $sortedValue) { $row[$sortedKey . '_sorted'] = $sortedValue; @@ -308,7 +368,7 @@ public function getDataFor($arrIds) if (isset($row['sort'])) { // @codingStandardsIgnoreStart - @\trigger_error( + @trigger_error( 'The sort key from the attribute file is deprecated since 2.1 and where removed in 3.0' . 'Use the key bin_sorted', E_USER_DEPRECATED @@ -317,7 +377,7 @@ public function getDataFor($arrIds) } } - $data[$result->id] = $row; + $data[$result['id']] = $row; } return $data; @@ -332,8 +392,6 @@ public function getDataFor($arrIds) */ public function setDataFor($arrValues) { - $tableName = $this->getMetaModel()->getTableName(); - $colName = $this->getColName(); foreach ($arrValues as $id => $value) { if (null === $value) { // The sort key be can remove in later version. @@ -344,16 +402,19 @@ public function setDataFor($arrValues) // Check single file or multiple file. if ($this->get('file_multiple')) { - $files = \serialize($files); + $files = serialize($files); } else { - $files = $files[0]; + $files = $files[0] ?? null; } - $this->connection->update( - $this->quoteReservedWord($tableName), - [$this->quoteReservedWord($colName) => $files], - [$this->quoteReservedWord('id') => $id] - ); + $this->connection + ->createQueryBuilder() + ->update($this->getMetaModel()->getTableName(), 't') + ->set('t.' . $this->getColName(), ':' . $this->getColName()) + ->where('t.id=:id') + ->setParameter($this->getColName(), $files) + ->setParameter('id', $id) + ->executeQuery(); } } @@ -370,7 +431,7 @@ public function getFilterOptions($idList, $usedOnly, &$arrCount = null) */ public function getAttributeSettingNames() { - return \array_merge( + return array_merge( parent::getAttributeSettingNames(), [ 'file_multiple', @@ -411,10 +472,10 @@ public function serializeData($mixValues) // Check single file or multiple file. if ($this->get('file_multiple')) { - return \serialize($data); + return serialize($data); } - return $data[0]; + return $data[0] ?? ''; } /** @@ -430,8 +491,8 @@ private function handleCustomFileTree(&$arrFieldDef) // Set root path of file chooser depending on contao version. $file = null; - if ($this->validator->isUuid($this->get('file_uploadFolder'))) { - $file = $this->fileRepository->findByUuid($this->get('file_uploadFolder')); + if ($this->validator->isUuid($this->get('file_uploadFolder') ?? '')) { + $file = $this->fileRepository->findByUuid($this->get('file_uploadFolder') ?? ''); } // Check if we have a file. @@ -447,8 +508,17 @@ private function handleCustomFileTree(&$arrFieldDef) $arrFieldDef['eval']['extensions'] = $this->get('file_validFileTypes'); } - if ($this->get('file_filesOnly')) { - $arrFieldDef['eval']['filesOnly'] = true; + switch ($this->get('file_filesOnly')) { + case '1': + // Files only. + $arrFieldDef['eval']['filesOnly'] = true; + break; + case '2': + // Folders only. + $arrFieldDef['eval']['files'] = false; + break; + default: + // Files and files possible. } } @@ -466,9 +536,7 @@ public function getFieldDefinition($arrOverrides = []) $widgetMode = $this->getOverrideValue('file_widgetMode', $arrOverrides); - if (('normal' !== $widgetMode) - && ((bool) $this->get('file_multiple')) - ) { + if (('normal' !== $widgetMode) && ((bool) $this->get('file_multiple'))) { $fieldDefinition['eval']['orderField'] = $this->getColName() . '__sort'; } @@ -506,82 +574,92 @@ public function widgetToValue($varValue, $itemId) /** * {@inheritDoc} + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ - protected function prepareTemplate(Template $template, $rowData, $settings) + protected function prepareTemplate(Template $objTemplate, $arrRowData, $objSettings) { - parent::prepareTemplate($template, $rowData, $settings); + parent::prepareTemplate($objTemplate, $arrRowData, $objSettings); + + /** @var array{ + * bin: list, + * value: list, + * path: list, + * meta: list, + * bin_sorted?: list, + * value_sorted?: list, + * path_sorted?: list, + * meta_sorted?: list + * }|null $value */ + $value = $arrRowData[$this->getColName()] ?? null; + if (null === $value) { + $value = ['bin' => [], 'value' => [], 'path' => [], 'meta' => []]; + } - // No data, nothing to do. - if (!$rowData[$this->getColName()]) { - return; + $showImage = (bool) $objSettings->get('file_showImage'); + + // No data and show image, check placeholder. + if ([] === $value['bin']) { + if (null === ($placeholder = $objSettings->get('file_placeholder'))) { + $objTemplate->files = []; + $objTemplate->src = []; + + return; + } + + $value['bin'][] = $placeholder; + $value['value'][] = StringUtil::binToUuid($placeholder); } $toolbox = clone $this->toolboxFile; + /** @psalm-suppress DeprecatedMethod */ $toolbox ->setBaseLanguage($this->getMetaModel()->getActiveLanguage()) ->setFallbackLanguage($this->getMetaModel()->getFallbackLanguage()) ->setLightboxId( - \sprintf( + sprintf( '%s.%s.%s', $this->getMetaModel()->getTableName(), - $settings->get('id'), - $rowData['id'] + (string) ($objSettings->get('id') ?? ''), + (string) ($arrRowData['id'] ?? '0') ) ) - ->setShowImages($settings->get('file_showImage')); + ->setShowImages($showImage); if ($this->get('file_validFileTypes')) { $toolbox->setAcceptedExtensions($this->get('file_validFileTypes')); } - if ($settings->get('file_imageSize')) { - $toolbox->setResizeImages($settings->get('file_imageSize')); + if (is_array($imageSize = $objSettings->get('file_imageSize'))) { + $toolbox->setResizeImages($imageSize); } - $value = $rowData[$this->getColName()]; - - if (isset($value['value'])) { - foreach ($value['value'] as $strFile) { - $toolbox->addPathById($strFile); - } - } elseif (\is_array($value)) { - foreach ($value as $strFile) { - $toolbox->addPathById($strFile); - } - } else { - $toolbox->addPathById($value); + foreach ($value['value'] ?? [] as $strFile) { + $toolbox->addPathById($strFile); } + $toolbox->withDownloadKeys( + ((bool) $objSettings->get('file_showLink')) && ((bool) $objSettings->get('file_protectedDownload')) + ); + $toolbox->resolveFiles(); - $data = $toolbox->sortFiles($settings->get('file_sortBy'), ($value['bin_sorted'] ?? [])); + $data = $toolbox->sortFiles($objSettings->get('file_sortBy') ?? 'name_asc', ($value['bin_sorted'] ?? [])); - $template->files = $data['files']; - $template->src = $data['source']; + $objTemplate->files = $data['files']; + $objTemplate->src = $data['source']; } - /** - * Quote the reserved platform key word. - * - * @param string $word The key word. - * - * @return string - */ - private function quoteReservedWord(string $word): string + private function fetchServiceForFallback(string $parameter, string $serviceName): null|object { - if (null === $this->platformReservedWord) { - try { - $this->platformReservedWord = $this->connection->getDatabasePlatform()->getReservedKeywordsList(); - } catch (DBALException $exception) { - // Add the not support key word list, if the platform has not a list of keywords. - $this->platformReservedWord = new NotSupportedKeywordList(); - } - } - - if (false === $this->platformReservedWord->isKeyword($word)) { - return $word; - } + // @codingStandardsIgnoreStart + @trigger_error( + '"'. $parameter . '" is missing. It has to be passed in the constructor.' . + 'Fallback will get removed in MetaModels 3.0', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd - return $this->connection->quoteIdentifier($word); + return System::getContainer()->get($serviceName); } } diff --git a/src/Attribute/FileOrder.php b/src/Attribute/FileOrder.php index e702c3c..9f0faf7 100644 --- a/src/Attribute/FileOrder.php +++ b/src/Attribute/FileOrder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Stefan Heimes - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,11 +24,8 @@ use Contao\StringUtil; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\DBALException; -use Doctrine\DBAL\Platforms\Keywords\KeywordList; use MetaModels\Attribute\ISimple; use MetaModels\Attribute\IInternal; -use MetaModels\AttributeFileBundle\Doctrine\DBAL\Platforms\Keywords\NotSupportedKeywordList; use MetaModels\IMetaModel; /** @@ -42,37 +40,30 @@ class FileOrder implements ISimple, IInternal * * @var IMetaModel */ - private $metaModel; + private IMetaModel $metaModel; /** * The column name. * * @var string */ - private $colName; + private string $colName; /** * The connection. * * @var Connection */ - private $connection; - - /** - * The platform reserved keyword list. - * - * @var KeywordList - */ - private $platformReservedWord; + private Connection $connection; /** * Create a new instance. * - * @param null $metaModel The MetaModel. + * @param IMetaModel $metaModel The MetaModel. * @param array $information The attribute information. * @param Connection $connection The connection. */ - public function __construct($metaModel, array $information, Connection $connection) + public function __construct(IMetaModel $metaModel, array $information, Connection $connection) { $this->metaModel = $metaModel; $this->colName = $information['colname']; @@ -84,7 +75,7 @@ public function __construct($metaModel, array $information, Connection $connecti */ public function getName() { - return null; + return ''; } /** @@ -203,11 +194,14 @@ public function widgetToValue($varValue, $itemId) public function setDataFor($arrValues) { foreach ($arrValues as $id => $value) { - $this->connection->update( - $this->quoteReservedWord($this->getMetaModel()->getTableName()), - [$this->quoteReservedWord($this->getColName()) => $value ?: $this->serializeData([])], - [$this->quoteReservedWord('id') => $id] - ); + $this->connection + ->createQueryBuilder() + ->update($this->getMetaModel()->getTableName(), 't') + ->set('t.' . $this->getColName(), ':' . $this->getColName()) + ->where('t.id=:id') + ->setParameter($this->getColName(), $value ?: $this->serializeData([])) + ->setParameter('id', $id) + ->executeQuery(); } } @@ -363,29 +357,4 @@ public function serializeData($value) { return \serialize($value); } - - /** - * Quote the reserved platform key word. - * - * @param string $word The key word. - * - * @return string - */ - private function quoteReservedWord(string $word): string - { - if (null === $this->platformReservedWord) { - try { - $this->platformReservedWord = $this->connection->getDatabasePlatform()->getReservedKeywordsList(); - } catch (DBALException $exception) { - // Add the not support key word list, if the platform has not a list of keywords. - $this->platformReservedWord = new NotSupportedKeywordList(); - } - } - - if (false === $this->platformReservedWord->isKeyword($word)) { - return $word; - } - - return $this->connection->quoteIdentifier($word); - } } diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php index 2fedbda..c5f9410 100644 --- a/src/ContaoManager/Plugin.php +++ b/src/ContaoManager/Plugin.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/attribute_file * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,6 +25,7 @@ use Contao\ManagerPlugin\Bundle\Config\BundleConfig; use Contao\ManagerPlugin\Bundle\Parser\ParserInterface; use MetaModels\AttributeFileBundle\MetaModelsAttributeFileBundle; +use MetaModels\ContaoFrontendEditingBundle\MetaModelsContaoFrontendEditingBundle; use MetaModels\CoreBundle\MetaModelsCoreBundle; /** @@ -34,13 +36,14 @@ class Plugin implements BundlePluginInterface /** * {@inheritdoc} */ - public function getBundles(ParserInterface $parser) + public function getBundles(ParserInterface $parser): array { return [ BundleConfig::create(MetaModelsAttributeFileBundle::class) ->setLoadAfter( [ - MetaModelsCoreBundle::class + MetaModelsCoreBundle::class, + MetaModelsContaoFrontendEditingBundle::class ] ) ->setReplace(['metamodelsattribute_file']) diff --git a/src/DependencyInjection/Configuration.php b/src/DependencyInjection/Configuration.php new file mode 100644 index 0000000..d945b14 --- /dev/null +++ b/src/DependencyInjection/Configuration.php @@ -0,0 +1,70 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2025 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\AttributeFileBundle\DependencyInjection; + +use Symfony\Component\Config\Definition\Builder\ArrayNodeDefinition; +use Symfony\Component\Config\Definition\Builder\TreeBuilder; +use Symfony\Component\Config\Definition\ConfigurationInterface; + +/** + * Adds the Contao configuration structure. + */ +final class Configuration implements ConfigurationInterface +{ + /** + * The debug flag. + * + * @var bool + */ + private bool $debug; + + /** + * Constructor. + * + * @param bool $debug The debug flag. + */ + public function __construct(bool $debug) + { + $this->debug = $debug; + } + + /** + * {@inheritDoc} + */ + public function getConfigTreeBuilder(): TreeBuilder + { + $treeBuilder = new TreeBuilder('metamodels_attribute_file'); + + $rootNode = $treeBuilder->getRootNode(); + assert($rootNode instanceof ArrayNodeDefinition); + $children = $rootNode->children(); + $children->booleanNode('enable_cache')->defaultValue(!$this->debug)->end(); + $children + ->scalarNode('cache_dir') + ->defaultValue('%metamodels.cache_dir%' . DIRECTORY_SEPARATOR . 'attribute_file') + ->end(); + $children->booleanNode('file_usage')->defaultValue(false)->end(); + + return $treeBuilder; + } +} diff --git a/src/DependencyInjection/MetaModelsAttributeFileExtension.php b/src/DependencyInjection/MetaModelsAttributeFileExtension.php index 44f0385..38d6790 100644 --- a/src/DependencyInjection/MetaModelsAttributeFileExtension.php +++ b/src/DependencyInjection/MetaModelsAttributeFileExtension.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2025 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,18 +13,29 @@ * @package MetaModels/attribute_file * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2025 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\AttributeFileBundle\DependencyInjection; +use InspiredMinds\ContaoFileUsage\ContaoFileUsageBundle; +use MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\DcaSetting\FileWidgetModeOptions; +use MetaModels\ContaoFrontendEditingBundle\MetaModelsContaoFrontendEditingBundle; +use Symfony\Component\Cache\Adapter\ArrayAdapter; +use Symfony\Component\Config\Definition\ConfigurationInterface; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Loader; +use function assert; +use function in_array; +use function is_array; +use function is_bool; + /** * This is the class that loads and manages the bundle configuration */ @@ -32,12 +43,89 @@ class MetaModelsAttributeFileExtension extends Extension { /** * {@inheritDoc} + * + * @SuppressWarnings(PHPMD.LongVariable) */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); $loader->load('factory.yml'); $loader->load('event_listener.yml'); $loader->load('services.yml'); + + $configuration = $this->getConfiguration($configs, $container); + assert($configuration instanceof Configuration); + $config = $this->processConfiguration($configuration, $configs); + $this->buildCacheService($container, $config); + + $bundles = $container->getParameter('kernel.bundles'); + assert(is_array($bundles)); + + // Load configuration for the frontend editing extension. + $frontendEditing = false; + if (in_array(MetaModelsContaoFrontendEditingBundle::class, $bundles, true)) { + $frontendEditing = true; + $loader->load('frontend_editing/event_listener.yml'); + } + $this->addFrontendEditingArgument($container, $frontendEditing); + + // Load configuration for the file usage extension. + if (in_array(ContaoFileUsageBundle::class, $bundles, true) && (bool) ($config['file_usage'] ?? false)) { + $loader->load('file_usage/services.yml'); + } + + // Schema manager + $typeNames = $container->hasParameter('metamodels.managed-schema-type-names') + ? $container->getParameter('metamodels.managed-schema-type-names') + : null; + $managedSchemaTypeNames = is_array($typeNames) ? $typeNames : []; + $managedSchemaTypeNames[] = 'file'; + $container->setParameter('metamodels.managed-schema-type-names', $managedSchemaTypeNames); + } + + /** + * {@inheritDoc} + */ + public function getConfiguration(array $config, ContainerBuilder $container): ?ConfigurationInterface + { + $debug = $container->getParameter('kernel.debug'); + assert(is_bool($debug)); + + return new Configuration($debug); + } + + /** + * Build the cache service. + * + * @param ContainerBuilder $container The container builder. + * @param array $config The configuration. + * + * @return void + */ + private function buildCacheService(ContainerBuilder $container, array $config): void + { + // If cache disabled, swap it out with the dummy cache. + if (!$config['enable_cache']) { + $cache = $container->getDefinition('metamodels.attribute_file.cache_system'); + $cache->setClass(ArrayAdapter::class); + $cache->setArguments([]); + $container->setParameter('metamodels.attribute_file.cache_dir', null); + return; + } + + $container->setParameter('metamodels.attribute_file.cache_dir', $config['cache_dir']); + } + + /** + * Add the frontend editing argument to service, who it used. + * + * @param ContainerBuilder $container The container builder. + * @param bool $frontendEditing Is frontend editing extension installed. + * + * @return void + */ + private function addFrontendEditingArgument(ContainerBuilder $container, bool $frontendEditing): void + { + $container->getDefinition(FileWidgetModeOptions::class)->setArgument('$frontendEditing', $frontendEditing); } } diff --git a/src/Doctrine/DBAL/Platforms/Keywords/NotSupportedKeywordList.php b/src/Doctrine/DBAL/Platforms/Keywords/NotSupportedKeywordList.php deleted file mode 100644 index 1d38824..0000000 --- a/src/Doctrine/DBAL/Platforms/Keywords/NotSupportedKeywordList.php +++ /dev/null @@ -1,60 +0,0 @@ - - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace MetaModels\AttributeFileBundle\Doctrine\DBAL\Platforms\Keywords; - -use Doctrine\DBAL\Platforms\Keywords\KeywordList; - -/** - * This is for platform that has not supported keyword list. - */ -class NotSupportedKeywordList extends KeywordList -{ - /** - * {@inheritDoc} - */ - protected function getKeywords(): array - { - return []; - } - - /** - * {@inheritDoc} - */ - public function getName() - { - return 'NotSupportedKeywordList'; - } - - /** - * {@inheritDoc} - */ - public function isKeyword($word): bool - { - return false; - } - - /** - * {@inheritDoc} - */ - protected function initializeKeywords(): void - { - // Do nothing - } -} diff --git a/src/EventListener/BuildAttributeListener.php b/src/EventListener/BuildAttributeListener.php index 9a23fab..979c121 100644 --- a/src/EventListener/BuildAttributeListener.php +++ b/src/EventListener/BuildAttributeListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/attribute_file * @author Stefan Heimes * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,11 +25,8 @@ use MetaModels\AttributeFileBundle\Attribute\File; use MetaModels\AttributeFileBundle\DcGeneral\AttributeFileDefinition; use MetaModels\DcGeneral\Events\MetaModel\BuildAttributeEvent; -use \ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; -/** - * Class Attribute - */ class BuildAttributeListener { /** @@ -41,7 +39,10 @@ class BuildAttributeListener public function buildAttribute(BuildAttributeEvent $event) { $attribute = $event->getAttribute(); - if (!($attribute instanceof File) || !$attribute->get('file_multiple')) { + if ( + !($attribute instanceof File) + || !$attribute->get('file_multiple') + ) { return; } @@ -50,15 +51,14 @@ public function buildAttribute(BuildAttributeEvent $event) $name = $attribute->getColName(); $nameSort = \sprintf('%s__sort', $name); - if ($properties->hasProperty($nameSort)) { - $this->addAttributeToDefinition($container, $name); - $properties->getProperty($name . '__sort')->setWidgetType('fileTreeOrder'); - - return; + if (!$properties->hasProperty($nameSort)) { + $properties->addProperty(new DefaultProperty($nameSort)); } - $properties->addProperty($property = new DefaultProperty($name . '__sort')); - $property->setWidgetType('fileTreeOrder'); + $properties->getProperty($nameSort) + ->setWidgetType('fileTreeOrder') + ->setLabel($nameSort) + ->setExtra(['tl_class' => 'hidden']); $this->addAttributeToDefinition($container, $name); } @@ -67,17 +67,19 @@ public function buildAttribute(BuildAttributeEvent $event) * Add attribute to MetaModels file attributes definition. * * @param ContainerInterface $container The metamodel data definition. - * * @param string $name The attribute name. * * @return void */ - private function addAttributeToDefinition(ContainerInterface $container, $name) + private function addAttributeToDefinition(ContainerInterface $container, string $name): void { if (!$container->hasDefinition('metamodels.file-attributes')) { $container->setDefinition('metamodels.file-attributes', new AttributeFileDefinition()); } - $container->getDefinition('metamodels.file-attributes')->add($name); + $definition = $container->getDefinition('metamodels.file-attributes'); + assert($definition instanceof AttributeFileDefinition); + + $definition->add($name); } } diff --git a/src/EventListener/BuildDataDefinitionListener.php b/src/EventListener/BuildDataDefinitionListener.php index 77b438e..b828905 100644 --- a/src/EventListener/BuildDataDefinitionListener.php +++ b/src/EventListener/BuildDataDefinitionListener.php @@ -21,6 +21,7 @@ use ContaoCommunityAlliance\DcGeneral\Factory\Event\BuildDataDefinitionEvent; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Property; +use MetaModels\AttributeFileBundle\DcGeneral\AttributeFileDefinition; /** * Class BuildDataDefinitionListener @@ -41,13 +42,16 @@ public function buildDataDefinition(BuildDataDefinitionEvent $event) return; } // All properties... - foreach ($container->getDefinition('metamodels.file-attributes')->get() as $propertyName) { + $definition = $container->getDefinition('metamodels.file-attributes'); + assert($definition instanceof AttributeFileDefinition); + foreach ($definition->get() as $propertyName) { // ... in all palettes ... foreach ($container->getPalettesDefinition()->getPalettes() as $palette) { // ... in any legend ... foreach ($palette->getLegends() as $legend) { // ... of the searched name ... - if (($legend->hasProperty($propertyName)) + if ( + ($legend->hasProperty($propertyName)) && ($container->getPropertiesDefinition()->hasProperty($propertyName . '__sort')) ) { // ... must have the order field as companion, visible only when the real property is. diff --git a/src/EventListener/BuildFrontendUploadListener.php b/src/EventListener/BuildFrontendUploadListener.php new file mode 100644 index 0000000..f616d08 --- /dev/null +++ b/src/EventListener/BuildFrontendUploadListener.php @@ -0,0 +1,312 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\AttributeFileBundle\EventListener; + +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; +use Contao\CoreBundle\InsertTag\InsertTagParser; +use Contao\FrontendUser; +use MetaModels\AttributeFileBundle\Attribute\File; +use MetaModels\DcGeneral\Events\MetaModel\BuildAttributeEvent; +use MetaModels\ViewCombination\ViewCombination; +use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; + +/** + * This event build the attribute for upload field, in the frontend editing scope. + * + * @SuppressWarnings(PHPMD.UnusedPrivateField) + */ +final class BuildFrontendUploadListener +{ + use RequestScopeDeterminatorAwareTrait; + + /** + * The view combinations. + * + * @var ViewCombination + */ + private ViewCombination $viewCombination; + + /** + * The token storage. + * + * @var TokenStorageInterface + */ + private TokenStorageInterface $tokenStorage; + + /** + * The property information from the input screen. + * + * @var array + */ + private array $information = []; + + /** + * The insert tag parser. + * + * @var InsertTagParser + */ + private InsertTagParser $insertTagParser; + + /** + * The constructor. + * + * @param ViewCombination $viewCombination The view combination. + * @param TokenStorageInterface $tokenStorage The token storage. + * @param InsertTagParser $insertTagParser The insert tag parser. + */ + public function __construct( + ViewCombination $viewCombination, + TokenStorageInterface $tokenStorage, + InsertTagParser $insertTagParser + ) { + $this->viewCombination = $viewCombination; + $this->tokenStorage = $tokenStorage; + $this->insertTagParser = $insertTagParser; + } + + /** + * Build the attribute for single upload field, in the frontend editing scope. + * + * @param BuildAttributeEvent $event The event. + * + * @return void + */ + public function __invoke(BuildAttributeEvent $event): void + { + if (!$this->wantToHandle($event)) { + return; + } + + $this->addWidgetModeInformationToProperty($event); + } + + /** + * Add widget mode information to the property extra information. + * + * @param BuildAttributeEvent $event The event. + * + * @return void + */ + private function addWidgetModeInformationToProperty(BuildAttributeEvent $event): void + { + $property = + $event->getContainer()->getPropertiesDefinition()->getProperty($event->getAttribute()->getColName()); + + $property->setWidgetType('uploadOnSteroids'); + + $extra = [ + 'doNotOverwrite' => $this->information['fe_widget_file_doNotOverwrite'], + 'deselect' => (bool) $this->information['fe_widget_file_deselect'], + 'delete' => (bool) $this->information['fe_widget_file_delete'], + 'uploadFolder' => $this->getUserHomeDir() ?? $this->getTargetFolder() ?? '', + 'extendFolder' => $this->getExtendFolder(), + 'normalizeExtendFolder' => (bool) $this->information['fe_widget_file_normalize_extend_folder'], + 'normalizeFilename' => (bool) $this->information['fe_widget_file_normalize_filename'], + 'prefixFilename' => $this->getPrefixFilename(), + 'postfixFilename' => $this->getPostfixFilename(), + 'storeFile' => true, + 'imageSize' => $this->information['fe_widget_file_imageSize'], + 'sortBy' => $this->information['fe_widget_file_sortBy'], + ]; + + $previewModes = ['fe_single_upload_preview', 'fe_multiple_upload_preview']; + if (\in_array($this->information['file_widgetMode'], $previewModes, true)) { + $extra['showThumbnail'] = true; + } + + $multipleModes = ['fe_multiple_upload', 'fe_multiple_upload_preview']; + $extra['multiple'] = false; + if (\in_array($this->information['file_widgetMode'], $multipleModes, true)) { + $extra['multiple'] = true; + } + + if ($this->storeFileToTempFolder($extra)) { + $extra['useTempFolder'] = true; + $extra['deleteTempFolder'] = true; + $extra['moveToDestination'] = true; + } + + $property->setExtra(\array_merge($property->getExtra(), $extra)); + + // @codingStandardsIgnoreStart + // TODO: support sorting file. Can be remove if this attribute support order not has hack. + // @codingStandardsIgnoreEnd + $properties = $event->getContainer()->getPropertiesDefinition(); + $propertyExtra = $property->getExtra(); + if (isset($propertyExtra['orderField']) && $properties->hasProperty($propertyExtra['orderField'])) { + $orderProperty = $properties->getProperty($propertyExtra['orderField']); + $properties->removeProperty($orderProperty); + } + } + + /** + * Use the user home directory as base folder, if is configured and the user is authenticated. + * + * @return string|null + */ + private function getUserHomeDir(): ?string + { + if (!$this->information['fe_widget_file_useHomeDir'] || (null === ($user = $this->tokenStorage->getToken()))) { + return null; + } + $feUser = $user->getUser(); + assert($feUser instanceof FrontendUser); + + if (((bool) $feUser->assignDir) && $feUser->homeDir) { + return $feUser->homeDir; + } + + return null; + } + + /** + * Use the target folder as base folder, if is configured and the user is not authenticated. + * + * @return string|null + */ + private function getTargetFolder(): ?string + { + return $this->information['fe_widget_file_uploadFolder'] ?: null; + } + + /** + * Get the extend folder. + * + * @return string|null + */ + private function getExtendFolder(): ?string + { + return $this->replaceInsertTagIfNeeded($this->information['fe_widget_file_extend_folder'] ?? null); + } + + /** + * Get the prefix for the filename. + * + * @return string|null + */ + private function getPrefixFilename(): ?string + { + return $this->replaceInsertTagIfNeeded($this->information['fe_widget_file_prefix_filename'] ?? null); + } + + /** + * Get the postfix for the filename. + * + * @return string|null + */ + private function getPostfixFilename(): ?string + { + return $this->replaceInsertTagIfNeeded($this->information['fe_widget_file_postfix_filename'] ?? null); + } + + private function replaceInsertTagIfNeeded(mixed $value): ?string + { + if (null === $value) { + return null; + } + assert(\is_string($value)); + + return \str_contains($value, '{{') ? $this->replaceInsertTag($value) : $value; + } + + /** + * Replace the insert tag - without converting to esi tags. + * + * @param string $replace The replacement. + * + * @return string + */ + private function replaceInsertTag(string $replace): string + { + return $this->insertTagParser->replaceInline($replace); + } + + /** + * Detect for store file in a temporary folder. + * + * @param array $extra The extra information. + * + * @return bool + */ + private function storeFileToTempFolder(array $extra): bool + { + return (isset($extra['extendFolder']) && $extra['extendFolder']) + // Test if in the extent folder path find insert tag. + && (\str_contains($extra['extendFolder'], '{{')); + } + + /** + * Detect if in the right scope and the attribute is configured as single upload field. + * + * @param BuildAttributeEvent $event The event. + * + * @return bool + */ + private function wantToHandle(BuildAttributeEvent $event): bool + { + $scopeDeterminator = $this->scopeDeterminator; + assert($scopeDeterminator instanceof RequestScopeDeterminator); + + return $scopeDeterminator->currentScopeIsFrontend() + && !$scopeDeterminator->currentScopeIsUnknown() + && $this->isSingleUploadField($event); + } + + /** + * Detect if is configured as single upload field. + * + * @param BuildAttributeEvent $event The event. + * + * @return bool + */ + private function isSingleUploadField(BuildAttributeEvent $event): bool + { + if (!(($attribute = $event->getAttribute()) instanceof File)) { + return false; + } + $inputScreen = $this->viewCombination->getScreen($event->getContainer()->getName()); + + if ((null === $inputScreen) || (!\is_array($properties = $inputScreen['properties'] ?? null))) { + return false; + } + + $supportedModes = [ + 'fe_single_upload', + 'fe_single_upload_preview', + 'fe_multiple_upload', + 'fe_multiple_upload_preview' + ]; + $information = $properties[\array_flip(\array_column($properties, 'attr_id'))[$attribute->get('id')]]; + if ( + !isset($information['file_widgetMode']) + || !\in_array($information['file_widgetMode'], $supportedModes, true) + ) { + return false; + } + + $this->information = $information; + + return true; + } +} diff --git a/src/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptions.php b/src/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptions.php index b791c00..1cfa8e2 100644 --- a/src/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptions.php +++ b/src/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptions.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/attribute_file * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -20,6 +21,7 @@ namespace MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\Attribute; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; /** * This class provide functions for remove type options, from the attribute table. @@ -35,15 +37,17 @@ class RemoveTypeOptions */ public function removeOption(GetPropertyOptionsEvent $event) { - $environment = $event->getEnvironment(); - if (('type' !== $event->getPropertyName()) - || ('tl_metamodel_attribute' !== $environment->getDataDefinition()->getName()) + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ( + ('type' !== $event->getPropertyName()) + || ('tl_metamodel_attribute' !== $dataDefinition->getName()) ) { return; } $options = $event->getOptions(); - if (!\array_key_exists('filesort', $options)) { + if ((null === $options) || !\array_key_exists('filesort', $options)) { return; } diff --git a/src/EventListener/DcGeneral/Table/DcaSetting/FeeFileImageSizeOptions.php b/src/EventListener/DcGeneral/Table/DcaSetting/FeeFileImageSizeOptions.php new file mode 100644 index 0000000..e63bba8 --- /dev/null +++ b/src/EventListener/DcGeneral/Table/DcaSetting/FeeFileImageSizeOptions.php @@ -0,0 +1,90 @@ + + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\DcaSetting; + +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use Doctrine\DBAL\Connection; +use MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsProvider; +use MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting\AbstractListener; +use MetaModels\IFactory; + +/** + * Add the options for the FEE file image size. + */ +final class FeeFileImageSizeOptions extends AbstractListener +{ + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + IFactory $factory, + Connection $connection, + private readonly ImageSizeOptionsProvider $optionsProvider, + ) { + parent::__construct($scopeDeterminator, $factory, $connection); + } + + /** + * Invoke the event. + * + * @param GetPropertyOptionsEvent $event The event. + * + * @return void + */ + public function __invoke(GetPropertyOptionsEvent $event): void + { + if ( + ('fe_widget_file_imageSize' !== $event->getPropertyName()) + || (false === $this->wantToHandle($event)) + || (false === $this->isAttributeFile($event)) + ) { + return; + } + + $this->optionsProvider->addOptions($event); + } + + /** + * If used attribute type of file. + * + * @param GetPropertyOptionsEvent $event The event. + * + * @return bool + */ + private function isAttributeFile(GetPropertyOptionsEvent $event): bool + { + $builder = $this->connection->createQueryBuilder(); + $builder + ->select('t.type') + ->from('tl_metamodel_attribute', 't') + ->where($builder->expr()->eq('t.id', ':id')) + ->setParameter('id', $event->getModel()->getProperty('attr_id')); + + $statement = $builder->executeQuery(); + if (0 === $statement->columnCount()) { + return false; + } + + $result = $statement->fetchAssociative(); + + return 'file' === ($result['type'] ?? null); + } +} diff --git a/src/EventListener/DcGeneral/Table/DcaSetting/FileWidgetModeOptions.php b/src/EventListener/DcGeneral/Table/DcaSetting/FileWidgetModeOptions.php new file mode 100644 index 0000000..37af20f --- /dev/null +++ b/src/EventListener/DcGeneral/Table/DcaSetting/FileWidgetModeOptions.php @@ -0,0 +1,136 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\DcaSetting; + +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use Doctrine\DBAL\Connection; +use MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting\AbstractListener; +use MetaModels\IFactory; + +/** + * Add the options for the file widget mode. + */ +class FileWidgetModeOptions extends AbstractListener +{ + /** + * Frontend editing extension installed. + * + * @var bool + */ + private bool $frontendEditing; + + /** + * {@inheritDoc} + */ + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + IFactory $factory, + Connection $connection, + bool $frontendEditing + ) { + parent::__construct($scopeDeterminator, $factory, $connection); + $this->frontendEditing = $frontendEditing; + } + + /** + * Invoke the event. + * + * @param GetPropertyOptionsEvent $event The event. + * + * @return void + */ + public function __invoke(GetPropertyOptionsEvent $event): void + { + if ( + ('file_widgetMode' !== $event->getPropertyName()) + || (false === $this->wantToHandle($event)) + || (false === $this->isAttributeFile($event)) + ) { + return; + } + + $this->addOptions($event); + } + + /** + * Add the options. + * + * @param GetPropertyOptionsEvent $event The event. + * + * @return void + */ + private function addOptions(GetPropertyOptionsEvent $event): void + { + $addOptions = ['downloads', 'gallery']; + if (true === $this->isFrontendEditingExtensionInstalled()) { + $addOptions = \array_merge( + $addOptions, + [ + 'fe_single_upload', + 'fe_single_upload_preview', + 'fe_multiple_upload', + 'fe_multiple_upload_preview' + ] + ); + } + + $event->setOptions(\array_values(\array_unique(\array_merge($event->getOptions() ?? [], $addOptions)))); + } + + /** + * If used attribute type of file. + * + * @param GetPropertyOptionsEvent $event The event. + * + * @return bool + */ + private function isAttributeFile(GetPropertyOptionsEvent $event): bool + { + $builder = $this->connection->createQueryBuilder(); + $builder + ->select('t.type') + ->from('tl_metamodel_attribute', 't') + ->where($builder->expr()->eq('t.id', ':id')) + ->setParameter('id', $event->getModel()->getProperty('attr_id')); + + $statement = $builder->executeQuery(); + if (0 === $statement->columnCount()) { + return false; + } + + $result = $statement->fetchAssociative(); + + return 'file' === ($result['type'] ?? null); + } + + /** + * Is frontend editing extension installed. + * + * @return bool + */ + private function isFrontendEditingExtensionInstalled(): bool + { + return $this->frontendEditing; + } +} diff --git a/src/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptions.php b/src/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptions.php index e027b89..c4da897 100644 --- a/src/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptions.php +++ b/src/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptions.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package MetaModels/attribute_file * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -20,6 +20,7 @@ namespace MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\FilterSetting; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; /** * This class provide functions for remove type options, from the filter setting table. @@ -35,17 +36,19 @@ class RemoveAttIdOptions */ public function removeOption(GetPropertyOptionsEvent $event) { - $environment = $event->getEnvironment(); - if (('attr_id' !== $event->getPropertyName()) - || ('tl_metamodel_filtersetting' !== $environment->getDataDefinition()->getName()) + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ( + ('attr_id' !== $event->getPropertyName()) + || ('tl_metamodel_filtersetting' !== $dataDefinition->getName()) ) { return; } - $options = $event->getOptions(); + $options = $event->getOptions() ?? []; foreach ($options as $key => $name) { $sortKey = $key . '__sort'; - if (\array_key_exists($sortKey, $options) && ('[file]' === \substr($name, -\strlen('[file]')))) { + if (\array_key_exists($sortKey, $options) && (\str_ends_with($name, '[file]'))) { unset($options[$sortKey]); } } diff --git a/src/EventListener/DcGeneral/Table/RenderSetting/FileImageSizeOptions.php b/src/EventListener/DcGeneral/Table/RenderSetting/FileImageSizeOptions.php new file mode 100644 index 0000000..c6e04bb --- /dev/null +++ b/src/EventListener/DcGeneral/Table/RenderSetting/FileImageSizeOptions.php @@ -0,0 +1,90 @@ + + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\RenderSetting; + +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use Doctrine\DBAL\Connection; +use MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsProvider; +use MetaModels\CoreBundle\EventListener\DcGeneral\Table\RenderSetting\AbstractListener; +use MetaModels\IFactory; + +/** + * Add the options for the file image size. + */ +final class FileImageSizeOptions extends AbstractListener +{ + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + IFactory $factory, + Connection $connection, + private readonly ImageSizeOptionsProvider $optionsProvider, + ) { + parent::__construct($scopeDeterminator, $factory, $connection); + } + + /** + * Invoke the event. + * + * @param GetPropertyOptionsEvent $event The event. + * + * @return void + */ + public function __invoke(GetPropertyOptionsEvent $event): void + { + if ( + ('file_imageSize' !== $event->getPropertyName()) + || (false === $this->wantToHandle($event)) + || (false === $this->isAttributeFile($event)) + ) { + return; + } + + $this->optionsProvider->addOptions($event); + } + + /** + * If used attribute type of file. + * + * @param GetPropertyOptionsEvent $event The event. + * + * @return bool + */ + private function isAttributeFile(GetPropertyOptionsEvent $event): bool + { + $builder = $this->connection->createQueryBuilder(); + $builder + ->select('t.type') + ->from('tl_metamodel_attribute', 't') + ->where($builder->expr()->eq('t.id', ':id')) + ->setParameter('id', $event->getModel()->getProperty('attr_id')); + + $statement = $builder->executeQuery(); + if (0 === $statement->columnCount()) { + return false; + } + + $result = $statement->fetchAssociative(); + + return 'file' === ($result['type'] ?? null); + } +} diff --git a/src/EventListener/HandleUpdateAttributeListener.php b/src/EventListener/HandleUpdateAttributeListener.php deleted file mode 100644 index a01702e..0000000 --- a/src/EventListener/HandleUpdateAttributeListener.php +++ /dev/null @@ -1,93 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace MetaModels\AttributeFileBundle\EventListener; - -use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; -use Doctrine\DBAL\Connection; -use MetaModels\Factory; -use MetaModels\Helper\TableManipulator; - -/** - * Class HandleUpdateAttributeListener - */ -class HandleUpdateAttributeListener extends BaseListener -{ - /** - * The doctrine dbal connection. - * - * @var Connection - */ - private $connection; - - /** - * The table manipulator. - * - * @var TableManipulator - */ - private $tableManipulator; - - /** - * HandleUpdateAttributeListener constructor. - * - * @param Factory $factory The attribute factory. - * @param Connection $connection The doctrine dbal connection. - * @param TableManipulator $tableManipulator The table manipulator. - */ - public function __construct(Factory $factory, Connection $connection, TableManipulator $tableManipulator) - { - parent::__construct($factory); - - $this->connection = $connection; - $this->tableManipulator = $tableManipulator; - } - - /** - * Handle the update of the file attribute, if switch on for file multiple. - * - * @param PostPersistModelEvent $event The event. - * - * @return void - * - * @throws \Exception If column not exist in the table. - */ - public function handleUpdateAttribute(PostPersistModelEvent $event) - { - $model = $event->getModel(); - - if (('file' !== $model->getProperty('type')) - || (!$model->getProperty('file_multiple')) - || ('tl_metamodel_attribute' !== $event->getEnvironment()->getDataDefinition()->getName()) - ) { - return; - } - - $metaModelsName = $this->getFactory()->translateIdToMetaModelName($model->getProperty('pid')); - $metaModel = $this->getFactory()->getMetaModel($metaModelsName); - $attributeName = $model->getProperty('colname') . '__sort'; - $tableColumns = $this->connection->getSchemaManager()->listTableColumns($metaModel->getTableName()); - - if (\array_key_exists($attributeName, $tableColumns)) { - return; - } - - $this->tableManipulator->createColumn($metaModel->getTableName(), $attributeName, 'blob NULL'); - } -} diff --git a/src/EventListener/ImageSizeOptionsListener.php b/src/EventListener/ImageSizeOptionsListener.php deleted file mode 100644 index cf8ed7b..0000000 --- a/src/EventListener/ImageSizeOptionsListener.php +++ /dev/null @@ -1,88 +0,0 @@ - - * @author Christian Schiffler - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace MetaModels\AttributeFileBundle\EventListener; - -use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; -use ContaoCommunityAlliance\DcGeneral\Data\DefaultDataProvider; - -/** - * Get the options for the image size. - */ -class ImageSizeOptionsListener -{ - /** - * Get property options for file image size in the render settings. - * - * @param GetPropertyOptionsEvent $event The event. - * - * @return void - */ - public function getPropertyOptions(GetPropertyOptionsEvent $event) - { - if (('file_imageSize' !== $event->getPropertyName()) - || ('tl_metamodel_rendersetting' !== $event->getEnvironment()->getDataDefinition()->getName()) - ) { - return; - } - - $sizes = $this->getThemeImageSizes(); - if (!$sizes) { - return; - } - - $options = $event->getOptions(); - $options['image_sizes'] = \array_replace($sizes, (array) $options['image_sizes']); - - $event->setOptions($options); - } - - /** - * Get the image sizes from the theme. - * - * @return array - */ - private function getThemeImageSizes() - { - $dataProvider = new DefaultDataProvider(); - $dataProvider->setBaseConfig(['source' => 'tl_image_size']); - - $config = $dataProvider->getEmptyConfig(); - $config->setFields(['id', 'name', 'width', 'height']); - $config->setSorting(['pid', 'name']); - - $collection = $dataProvider->fetchAll($config); - if (!$collection->count()) { - return []; - } - - $sizes = []; - foreach ($collection as $model) { - $sizes[$model->getProperty('id')] = \sprintf( - '%s (%sx%s)', - $model->getProperty('name'), - $model->getProperty('width'), - $model->getProperty('height') - ); - } - - return $sizes; - } -} diff --git a/src/EventListener/ImageSizeOptionsProvider.php b/src/EventListener/ImageSizeOptionsProvider.php new file mode 100644 index 0000000..b1eae6e --- /dev/null +++ b/src/EventListener/ImageSizeOptionsProvider.php @@ -0,0 +1,82 @@ + + * @author Christian Schiffler + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\AttributeFileBundle\EventListener; + +use Contao\CoreBundle\Image\ImageSizes; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * Get the options for the image size. + */ +class ImageSizeOptionsProvider +{ + public function __construct( + private readonly ImageSizes $imageSizes, + private readonly TranslatorInterface $translator, + ) { + } + + /** + * Add the options. + * + * @param GetPropertyOptionsEvent $event The event. + * + * @return void + */ + public function addOptions(GetPropertyOptionsEvent $event): void + { + $options = $this->imageSizes->getAllOptions(); + $optionsFull = []; + foreach ($options as $section => $sizeNames) { + $optionsFull[$this->translateSizeName($section)] = $this->translateSection($section, $sizeNames); + } + + $event->setOptions($optionsFull); + } + + private function translateSection(string $section, array $sizeNames): array + { + if (!\in_array($section, ['relative', 'exact'], true)) { + return $sizeNames; + } + + $optionSection = []; + foreach ($sizeNames as $currentLabel) { + $optionSection[$currentLabel] = $this->translateSizeName($currentLabel); + } + return $optionSection; + } + + private function translateSizeName(string $sizeName): string + { + $key = 'MSC.' . $sizeName . '.0'; + if ($key !== $label = $this->translator->trans($key, [], 'contao_default')) { + return $label; + } + $key = 'MSC.' . $sizeName; + if ($key !== $label = $this->translator->trans($key, [], 'contao_default')) { + return $label; + } + + return $sizeName; + } +} diff --git a/src/FileUsage/FileUsageProvider.php b/src/FileUsage/FileUsageProvider.php new file mode 100644 index 0000000..1b167a2 --- /dev/null +++ b/src/FileUsage/FileUsageProvider.php @@ -0,0 +1,177 @@ + + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\AttributeFileBundle\FileUsage; + +use Contao\CoreBundle\Csrf\ContaoCsrfTokenManager; +use Contao\FilesModel; +use Contao\Model\Collection; +use Contao\StringUtil; +use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use InspiredMinds\ContaoFileUsage\Provider\FileUsageProviderInterface; +use InspiredMinds\ContaoFileUsage\Result\ResultInterface; +use InspiredMinds\ContaoFileUsage\Result\ResultsCollection; +use MetaModels\AttributeFileBundle\Attribute\File; +use MetaModels\CoreBundle\FileUsage\MetaModelsMultipleResult; +use MetaModels\CoreBundle\FileUsage\MetaModelsSingleResult; +use MetaModels\IFactory; +use MetaModels\IMetaModel; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; + +/** + * This class supports the Contao extension 'file usage'. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class FileUsageProvider implements FileUsageProviderInterface +{ + private string $refererId = ''; + + public function __construct( + private readonly IFactory $factory, + private readonly UrlGeneratorInterface $urlGenerator, + private readonly RequestStack $requestStack, + private readonly ContaoCsrfTokenManager $csrfTokenManager, + private readonly string $csrfTokenName, + ) { + } + + public function find(): ResultsCollection + { + $this->refererId = $this->requestStack->getCurrentRequest()?->attributes->get('_contao_referer_id') ?? ''; + + $allTables = $this->factory->collectNames(); + + $collection = new ResultsCollection(); + foreach ($allTables as $table) { + $collection->mergeCollection($this->processTable($table)); + } + + return $collection; + } + + private function processTable(string $table): ResultsCollection + { + $collection = new ResultsCollection(); + $metaModel = $this->factory->getMetaModel($table); + assert($metaModel instanceof IMetaModel); + + $allIds = $metaModel->getIdsFromFilter($metaModel->getEmptyFilter()); + foreach ($metaModel->getAttributes() as $attribute) { + if (!$attribute instanceof File) { + continue; + } + + $attributeName = $attribute->getColName(); + + $allData = $attribute->getDataFor($allIds); + if ($attribute->get('file_multiple')) { + foreach ($allData as $itemId => $selectedFiles) { + $collection->mergeCollection( + $this->addMultipleFileReferences($selectedFiles['value'], $table, $attributeName, $itemId) + ); + } + continue; + } + + foreach ($allData as $itemId => $selectedFiles) { + if ([] === $selectedFiles['value']) { + continue; + } + $collection->addResult( + $selectedFiles['value'][0], + $this->createFileResult($table, $attributeName, $itemId, false) + ); + } + } + + return $collection; + } + + private function addMultipleFileReferences( + array $fileUuids, + string $tableName, + string $attributeName, + string $itemId, + ): ResultsCollection { + $collection = new ResultsCollection(); + foreach ($fileUuids as $uuid) { + $collection->addResult($uuid, $this->createFileResult($tableName, $attributeName, $itemId, true)); + // Also add children, if the reference is a folder. + $file = FilesModel::findByUuid($uuid); + if (null !== $file && 'folder' === $file->type) { + $files = FilesModel::findByPid($uuid); + if (null === $files) { + continue; + } + assert($files instanceof Collection); + foreach ($files as $child) { + $collection->addResult( + StringUtil::binToUuid($child->uuid), + $this->createFileResult($tableName, $attributeName, $itemId, true) + ); + } + } + } + + return $collection; + } + + private function createFileResult( + string $tableName, + string $attributeName, + string $itemId, + bool $isMultiple + ): ResultInterface { + if ($isMultiple) { + return new MetaModelsMultipleResult( + $tableName, + $attributeName, + $itemId, + $this->urlGenerator->generate( + 'metamodels.metamodel', + [ + 'tableName' => $tableName, + 'act' => 'edit', + 'id' => ModelId::fromValues($tableName, $itemId)->getSerialized(), + 'ref' => $this->refererId, + 'rt' => $this->csrfTokenManager->getToken($this->csrfTokenName)->getValue(), + ] + ) + ); + } + + return new MetaModelsSingleResult( + $tableName, + $attributeName, + $itemId, + $this->urlGenerator->generate( + 'metamodels.metamodel', + [ + 'tableName' => $tableName, + 'act' => 'edit', + 'id' => ModelId::fromValues($tableName, $itemId)->getSerialized(), + 'ref' => $this->refererId, + 'rt' => $this->csrfTokenManager->getToken($this->csrfTokenName)->getValue(), + ] + ) + ); + } +} diff --git a/src/Helper/UpgradeHandler.php b/src/Helper/UpgradeHandler.php deleted file mode 100644 index 6529195..0000000 --- a/src/Helper/UpgradeHandler.php +++ /dev/null @@ -1,118 +0,0 @@ - - * @author Sven Baumann - * @author Richard Henkenjohann - * @copyright 2012-2020 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace MetaModels\AttributeFileBundle\Helper; - -use Doctrine\DBAL\Connection; - -/** - * Upgrade handler class that changes structural changes in the database. - * This should rarely be necessary but sometimes we need it. - */ -class UpgradeHandler -{ - /** - * The database to use. - * - * @var Connection - */ - private $connection; - - /** - * The cache of table schemas. - * - * @var array - */ - private $schemaCache = []; - - /** - * Create a new instance. - * - * @param Connection $connection The database connection to use. - */ - public function __construct(Connection $connection) - { - $this->connection = $connection; - } - - /** - * Perform all upgrade steps. - * - * @return void - */ - public function perform() - { - $this->ensureOrderColumnExists(); - } - - /** - * Ensure that the order column exists. - * - * @return void - */ - private function ensureOrderColumnExists() - { - $attributes = $this - ->connection - ->createQueryBuilder() - ->select('metamodel.tableName', 'attribute.colname') - ->from('tl_metamodel_attribute', 'attribute') - ->leftJoin('attribute', 'tl_metamodel', 'metamodel', 'metamodel.id=attribute.pid') - ->where('attribute.type=:type') - ->setParameter('type', 'file') - ->andWhere('attribute.file_multiple=:multiple') - ->setParameter('multiple', '1') - ->execute(); - - while ($row = $attributes->fetch(\PDO::FETCH_OBJ)) { - if ($this->fieldExists($row->tableName, $row->colname . '__sort')) { - continue; - } - $this - ->connection - ->exec( - \sprintf( - 'ALTER TABLE %1$s ADD COLUMN %2$s__sort %3$s', - $row->tableName, - $row->colname, - 'blob NULL' - ) - ); - } - } - - /** - * Test if a column exists in a table. - * - * @param string $tableName Table name. - * @param string $columnName Column name. - * - * @return bool - */ - private function fieldExists($tableName, $columnName): bool - { - if (!\array_key_exists($tableName, $this->schemaCache)) { - $this->schemaCache[$tableName] = $this->connection->getSchemaManager()->listTableColumns($tableName); - } - - return isset($this->schemaCache[$tableName][$columnName]); - } -} diff --git a/src/Migration/AddProtectedDownloadMigration.php b/src/Migration/AddProtectedDownloadMigration.php new file mode 100644 index 0000000..3e5d2d9 --- /dev/null +++ b/src/Migration/AddProtectedDownloadMigration.php @@ -0,0 +1,138 @@ + + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\AttributeFileBundle\Migration; + +use Contao\CoreBundle\Migration\AbstractMigration; +use Contao\CoreBundle\Migration\MigrationResult; +use Doctrine\DBAL\Connection; +use MetaModels\Helper\TableManipulator; + +/** + * This migration add file_protectedDownload + * and changes set to 1 if file_showLink is set. + */ +class AddProtectedDownloadMigration extends AbstractMigration +{ + /** + * The database connection. + * + * @var Connection + */ + private Connection $connection; + + /** + * Table manipulator. + * + * @var TableManipulator + */ + protected TableManipulator $tableManipulator; + + /** + * Create a new instance. + * + * @param Connection $connection The database connection. + * @param TableManipulator $tableManipulator The table manipulator. + */ + public function __construct(Connection $connection, TableManipulator $tableManipulator) + { + $this->connection = $connection; + $this->tableManipulator = $tableManipulator; + } + + /** + * Return the name. + * + * @return string + */ + public function getName(): string + { + return 'Add checkbox "Protected download" in MetaModels render-settings if not exist and set to checked if ' . + 'checkbox "Create link as file download" is set as backward compatibility. If you do not need this, ' . + 'remove the protection, as no cookies need to be set for this.'; + } + + /** + * Must only run if: + * - the MM tables are present AND + * - there are some columns defined + * + * @return bool + */ + public function shouldRun(): bool + { + $schemaManager = $this->connection->createSchemaManager(); + + if (!$schemaManager->tablesExist(['tl_metamodel', 'tl_metamodel_rendersetting'])) { + return false; + } + + if ( + $this->fieldExists('tl_metamodel_rendersetting', 'file_showLink') + && !$this->fieldExists('tl_metamodel_rendersetting', 'file_protectedDownload') + ) { + return true; + } + + return false; + } + + /** + * Create the missing columns and copy existing values; + * drop column get_land manually in install tool. + * + * @return MigrationResult + */ + public function run(): MigrationResult + { + if (!$this->fieldExists('tl_metamodel_rendersetting', 'file_protectedDownload')) { + $this->tableManipulator->createColumn( + 'tl_metamodel_rendersetting', + 'file_protectedDownload', + 'char(1) NOT NULL default \'\'' + ); + + $this->connection->createQueryBuilder() + ->update('tl_metamodel_rendersetting', 't') + ->set('t.file_protectedDownload', 't.file_showLink') + ->executeQuery(); + + return new MigrationResult(true, 'Adjusted table tl_metamodel_rendersetting with file_protectedDownload'); + } + + return new MigrationResult(true, 'Nothing to do.'); + } + + /** + * Check is a table column exists. + * + * @param string $tableName Table name. + * @param string $columnName Column name. + * + * @return bool + */ + private function fieldExists(string $tableName, string $columnName): bool + { + $columns = $this->connection->createSchemaManager()->listTableColumns($tableName); + + return isset($columns[strtolower($columnName)]); + } +} diff --git a/src/Migration/AddSortFieldMigration.php b/src/Migration/AddSortFieldMigration.php new file mode 100644 index 0000000..41534ce --- /dev/null +++ b/src/Migration/AddSortFieldMigration.php @@ -0,0 +1,208 @@ + + * @author Kim Wormer + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\AttributeFileBundle\Migration; + +use Contao\CoreBundle\Migration\AbstractMigration; +use Contao\CoreBundle\Migration\MigrationResult; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Result; +use MetaModels\Helper\TableManipulator; + +/** + * This migration add column *__sort for every + * file attribute column if option multiple set. + */ +class AddSortFieldMigration extends AbstractMigration +{ + /** + * The database connection. + * + * @var Connection + */ + private Connection $connection; + + /** + * Table manipulator. + * + * @var TableManipulator + */ + protected TableManipulator $tableManipulator; + + /** + * Create a new instance. + * + * @param Connection $connection The database connection. + * @param TableManipulator $tableManipulator The table manipulator. + */ + public function __construct(Connection $connection, TableManipulator $tableManipulator) + { + $this->connection = $connection; + $this->tableManipulator = $tableManipulator; + } + + /** + * Return the name. + * + * @return string + */ + public function getName(): string + { + return 'Add column *__sort for every file attribute column if option multiple set.'; + } + + /** + * Must only run if: + * - the MM tables are present AND + * - there are some columns defined + * + * @return bool + * + * @throws Exception + */ + public function shouldRun(): bool + { + $schemaManager = $this->connection->createSchemaManager(); + + if (!$schemaManager->tablesExist(['tl_metamodel', 'tl_metamodel_attribute'])) { + return false; + } + + if (!$this->fieldExists('tl_metamodel_attribute', 'file_multiple')) { + return false; + } + + if ($this->countMissingSortColumns($this->getFileAttributes())) { + return true; + } + + return false; + } + + /** + * Create the missing columns *__sort for attribute file. + * + * @return MigrationResult + * + * @throws Exception + */ + public function run(): MigrationResult + { + $attributes = $this->getFileAttributes(); + $rows = $attributes->fetchAllAssociative(); + + if ( + !$this->fieldExists('tl_metamodel_attribute', 'file_multiple') + && !$attributes->rowCount() + ) { + return new MigrationResult(true, 'Nothing to do.'); + } + + $messages = []; + foreach ($rows as $row) { + if ($this->fieldExists($row['tableName'], $row['colname'] . '__sort')) { + continue; + } + + $this->tableManipulator->createColumn( + $row['tableName'], + $row['colname'] . '__sort', + 'blob NULL' + ); + + $messages[] = \sprintf('%s: %s__sort', $row['tableName'], $row['colname']); + } + + return new MigrationResult( + true, + \sprintf('Add columns for attribute file: %s', \implode(', ', $messages)) + ); + } + + /** + * Get file attributes. + * + * @return Result Returns database result. + * + * @throws \Doctrine\DBAL\Exception The DBAL exception. + */ + private function getFileAttributes(): Result + { + return $this + ->connection + ->createQueryBuilder() + ->select('metamodel.tableName, attribute.colname') + ->from('tl_metamodel_attribute', 'attribute') + ->leftJoin('attribute', 'tl_metamodel', 'metamodel', 'metamodel.id=attribute.pid') + ->where('attribute.type=:type') + ->setParameter('type', 'file') + ->andWhere('attribute.file_multiple=:multiple') + ->setParameter('multiple', '1') + ->executeQuery(); + } + + /** + * Count missing sort columns. + * + * @param Result $attributes The attributes. + * + * @return int Returns columns count. + * + * @throws Exception + */ + private function countMissingSortColumns(Result $attributes): int + { + $countColumns = 0; + $rows = $attributes->fetchAllAssociative(); + + foreach ($rows as $row) { + if ( + !$this->fieldExists($row['tableName'], $row['colname']) + || $this->fieldExists($row['tableName'], $row['colname'] . '__sort') + ) { + continue; + } + + $countColumns++; + } + + return $countColumns; + } + + /** + * Check if a table column exists. + * + * @param string $tableName Table name. + * @param string $columnName Column name. + * + * @return bool + * + * @throws Exception + */ + private function fieldExists(string $tableName, string $columnName): bool + { + $columns = $this->connection->createSchemaManager()->listTableColumns($tableName); + + return isset($columns[\strtolower($columnName)]); + } +} diff --git a/src/Resources/config/event_listener.yml b/src/Resources/config/event_listener.yml index d6a0af8..8ab2146 100644 --- a/src/Resources/config/event_listener.yml +++ b/src/Resources/config/event_listener.yml @@ -1,56 +1,79 @@ services: - metamodels.attribute_file.event_listener.image_size_options: - class: MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsListener - public: true - tags: - - name: kernel.event_listener - event: dc-general.view.contao2backend.get-property-options - method: getPropertyOptions - - metamodels.attribute_file.event_listener.remove_type_options: - class: MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\Attribute\RemoveTypeOptions - public: true - tags: - - name: kernel.event_listener - event: dc-general.view.contao2backend.get-property-options - method: removeOption - priority: -1 - - metamodels.attribute_file.event_listener.remove_attid_options: - class: MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\FilterSetting\RemoveAttIdOptions - public: true - tags: - - name: kernel.event_listener - event: dc-general.view.contao2backend.get-property-options - method: removeOption - priority: -1 - - metamodels.attribute_file.event_listener.build_attribute: - class: MetaModels\AttributeFileBundle\EventListener\BuildAttributeListener - public: true - tags: - - name: kernel.event_listener - event: metamodels.dc-general.events.metamodel.build.attribute - method: buildAttribute - - metamodels.attribute_file.event_listener.build-data-definition: - class: MetaModels\AttributeFileBundle\EventListener\BuildDataDefinitionListener - public: true - tags: - - name: kernel.event_listener - event: dc-general.factory.build-data-definition - method: buildDataDefinition - priority: 0 - - metamodels.attribute_file.event_listener.post-persist: - class: MetaModels\AttributeFileBundle\EventListener\HandleUpdateAttributeListener - arguments: - - "@metamodels.factory" - - "@database_connection" - - "@metamodels.table_manipulator" - public: true - tags: - - name: kernel.event_listener - event: dc-general.model.post-persist - method: handleUpdateAttribute - priority: -1 + metamodels.attribute_file.event_listener.image_size_options: + class: MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsProvider + arguments: + $imageSizes: '@contao.image.sizes' + $translator: '@translator' + + metamodels.attribute_file.event_listener.remove_type_options: + class: MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\Attribute\RemoveTypeOptions + public: true + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.get-property-options + method: removeOption + priority: -1 + + metamodels.attribute_file.event_listener.remove_attid_options: + class: MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\FilterSetting\RemoveAttIdOptions + public: true + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.get-property-options + method: removeOption + priority: -1 + + metamodels.attribute_file.event_listener.build_attribute: + class: MetaModels\AttributeFileBundle\EventListener\BuildAttributeListener + public: true + tags: + - name: kernel.event_listener + event: metamodels.dc-general.events.metamodel.build.attribute + method: buildAttribute + + metamodels.attribute_file.event_listener.build-data-definition: + class: MetaModels\AttributeFileBundle\EventListener\BuildDataDefinitionListener + public: true + tags: + - name: kernel.event_listener + event: dc-general.factory.build-data-definition + method: buildDataDefinition + priority: 0 + + MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\DcaSetting\FileWidgetModeOptions: + public: false + arguments: + $scopeDeterminator: '@cca.dc-general.scope-matcher' + $factory: '@MetaModels\IFactory' + $connection: '@database_connection' + # The $frontendEditing argument where set in the extension. + $frontendEditing: ~ + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.get-property-options + + MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\DcaSetting\FeeFileImageSizeOptions: + public: false + arguments: + $scopeDeterminator: '@cca.dc-general.scope-matcher' + $factory: '@MetaModels\IFactory' + $connection: '@database_connection' + $optionsProvider: '@metamodels.attribute_file.event_listener.image_size_options' + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.get-property-options + + MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\RenderSetting\FileImageSizeOptions: + public: false + arguments: + $scopeDeterminator: '@cca.dc-general.scope-matcher' + $factory: '@MetaModels\IFactory' + $connection: '@database_connection' + $optionsProvider: '@metamodels.attribute_file.event_listener.image_size_options' + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.get-property-options + + MetaModels\AttributeFileBundle\Schema\DoctrineSchemaGenerator: + tags: + - { name: 'metamodels.schema-generator.doctrine' } diff --git a/src/Resources/config/factory.yml b/src/Resources/config/factory.yml index b405e8e..fe9fcaa 100644 --- a/src/Resources/config/factory.yml +++ b/src/Resources/config/factory.yml @@ -4,7 +4,7 @@ services: arguments: - "@database_connection" - "@metamodels.table_manipulator" - - "@metamodels.attribute_file.toolbox.file" + - '@MetaModels\Helper\ToolboxFile' - "@=service('contao.framework').getAdapter('Contao\\\\StringUtil')" - "@=service('contao.framework').getAdapter('Contao\\\\Validator')" - "@=service('contao.framework').getAdapter('Contao\\\\FilesModel')" diff --git a/src/Resources/config/file_usage/services.yml b/src/Resources/config/file_usage/services.yml new file mode 100644 index 0000000..63e41d7 --- /dev/null +++ b/src/Resources/config/file_usage/services.yml @@ -0,0 +1,11 @@ +services: + MetaModels\AttributeFileBundle\FileUsage\FileUsageProvider: + public: true + arguments: + $factory: '@metamodels.factory' + $urlGenerator: '@router' + $requestStack: '@request_stack' + $csrfTokenManager: '@contao.csrf.token_manager' + $csrfTokenName: '%contao.csrf_token_name%' + tags: + - { name: contao_file_usage.provider } diff --git a/src/Resources/config/frontend_editing/event_listener.yml b/src/Resources/config/frontend_editing/event_listener.yml new file mode 100644 index 0000000..5bdf9b5 --- /dev/null +++ b/src/Resources/config/frontend_editing/event_listener.yml @@ -0,0 +1,15 @@ +services: + MetaModels\AttributeFileBundle\EventListener\BuildFrontendUploadListener: + public: false + calls: + - method: setScopeDeterminator + arguments: + - '@cca.dc-general.scope-matcher' + arguments: + - '@metamodels.view_combination' + - '@security.token_storage' + - '@contao.insert_tag.parser' + tags: + - name: kernel.event_listener + event: metamodels.dc-general.events.metamodel.build.attribute + priority: -128 diff --git a/src/Resources/config/services.yml b/src/Resources/config/services.yml index 8a24cef..9c59da6 100644 --- a/src/Resources/config/services.yml +++ b/src/Resources/config/services.yml @@ -1,7 +1,34 @@ services: metamodels.attribute_file.toolbox.file: - class: MetaModels\Helper\ToolboxFile + deprecated: + package: metamodels /attribute_file + version: 2.1 + message: 'The "%alias_id%" service is deprecated. Use the "@MetaModels\Helper\ToolboxFile".' + alias: MetaModels\Helper\ToolboxFile public: true + + metamodels.attribute_file.cache_system: + class: Symfony\Component\Cache\Adapter\FilesystemAdapter + public: false arguments: - - "@contao.image.image_factory" - - "%kernel.project_dir%" + - "%metamodels.attribute_file.cache_dir%" + + metamodels.attribute_file.cache: + class: Symfony\Component\Cache\Adapter\FilesystemAdapter + public: false + arguments: + - "@metamodels.attribute_file.cache_system" + + MetaModels\AttributeFileBundle\Migration\AddSortFieldMigration: + arguments: + - '@database_connection' + - '@metamodels.table_manipulator' + tags: + - name: contao.migration + + MetaModels\AttributeFileBundle\Migration\AddProtectedDownloadMigration: + arguments: + - '@database_connection' + - '@metamodels.table_manipulator' + tags: + - name: contao.migration diff --git a/src/Resources/contao/config/runonce.php b/src/Resources/contao/config/runonce.php deleted file mode 100644 index 43a409b..0000000 --- a/src/Resources/contao/config/runonce.php +++ /dev/null @@ -1,24 +0,0 @@ - - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -// Let our handler handle the necessary steps. -$handler = new MetaModels\AttributeFileBundle\Helper\UpgradeHandler( - \Contao\System::getContainer()->get('database_connection') -); -$handler->perform(); diff --git a/src/Resources/contao/dca/tl_metamodel_attribute.php b/src/Resources/contao/dca/tl_metamodel_attribute.php index 4cf9da4..32a4fa3 100644 --- a/src/Resources/contao/dca/tl_metamodel_attribute.php +++ b/src/Resources/contao/dca/tl_metamodel_attribute.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author David Molineus * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,37 +33,48 @@ ]; $GLOBALS['TL_DCA']['tl_metamodel_attribute']['fields']['file_customFiletree'] = [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_customFiletree'], - 'inputType' => 'checkbox', - 'sql' => 'char(1) NOT NULL default \'\'', - 'eval' => ['submitOnChange' => true, 'tl_class' => 'w50'] + 'label' => 'file_customFiletree.label', + 'description' => 'file_customFiletree.description', + 'inputType' => 'checkbox', + 'sql' => 'char(1) NOT NULL default \'\'', + 'eval' => ['submitOnChange' => true, 'tl_class' => 'w50'] ]; $GLOBALS['TL_DCA']['tl_metamodel_attribute']['fields']['file_multiple'] = [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_multiple'], - 'inputType' => 'checkbox', - 'sql' => 'char(1) NOT NULL default \'\'', - 'eval' => ['tl_class' => 'w50'] + 'label' => 'file_multiple.label', + 'description' => 'file_multiple.description', + 'inputType' => 'checkbox', + 'sql' => 'char(1) NOT NULL default \'\'', + 'eval' => ['tl_class' => 'w50'] ]; $GLOBALS['TL_DCA']['tl_metamodel_attribute']['fields']['file_uploadFolder'] = [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_uploadFolder'], - 'exclude' => true, - 'inputType' => 'fileTree', - 'sql' => 'blob NULL', - 'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'] + 'label' => 'file_uploadFolder.label', + 'description' => 'file_uploadFolder.description', + 'exclude' => true, + 'inputType' => 'fileTree', + 'sql' => 'blob NULL', + 'eval' => ['fieldType' => 'radio', 'tl_class' => 'clr'] ]; $GLOBALS['TL_DCA']['tl_metamodel_attribute']['fields']['file_validFileTypes'] = [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_validFileTypes'], - 'inputType' => 'text', - 'sql' => 'varchar(255) NOT NULL default \'\'', - 'eval' => ['maxlength' => 255, 'tl_class' => 'w50'] + 'label' => 'file_validFileTypes.label', + 'description' => 'file_validFileTypes.description', + 'inputType' => 'text', + 'sql' => 'varchar(255) NOT NULL default \'\'', + 'eval' => ['maxlength' => 255, 'tl_class' => 'w50'] ]; $GLOBALS['TL_DCA']['tl_metamodel_attribute']['fields']['file_filesOnly'] = [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_filesOnly'], - 'inputType' => 'checkbox', - 'sql' => 'char(1) NOT NULL default \'\'', - 'eval' => ['tl_class' => 'w50 m12'] + 'label' => 'file_filesOnly.label', + 'description' => 'file_filesOnly.description', + 'inputType' => 'select', + 'options' => ['', '1', '2'], + 'reference' => [ + '' => 'file_filesOnly_options.allow_both', + '1' => 'file_filesOnly_options.allow_files', + '2' => 'file_filesOnly_options.allow_folder', + ], + 'eval' => ['tl_class' => 'w50'], + 'sql' => ['type' => 'string', 'length' => 1, 'fixed' => true, 'default' => ''] ]; diff --git a/src/Resources/contao/dca/tl_metamodel_dcasetting.php b/src/Resources/contao/dca/tl_metamodel_dcasetting.php index 99e4ec7..ba69c80 100644 --- a/src/Resources/contao/dca/tl_metamodel_dcasetting.php +++ b/src/Resources/contao/dca/tl_metamodel_dcasetting.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,16 +15,238 @@ * @author Stefan Heimes * @author Andreas Isaak * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ +use Contao\BackendUser; +use Contao\System; +use MetaModels\ContaoFrontendEditingBundle\MetaModelsContaoFrontendEditingBundle; + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['metasubselectpalettes']['attr_id']['file'] = [ 'presentation' => [ - 'tl_class' + 'tl_class', + 'be_template', ], 'functions' => [ - 'mandatory' + 'mandatory', + 'file_widgetMode' + ], + 'overview' => [ + 'searchable', ] ]; + +$GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['file_widgetMode'] = [ + 'label' => 'file_widgetMode.label', + 'description' => 'file_widgetMode.description', + 'exclude' => true, + 'inputType' => 'radio', + 'default' => 'normal', + 'options' => ['normal'], + 'reference' => [ + 'normal' => 'file_widgetModes.normal', + 'downloads' => 'file_widgetModes.downloads', + 'gallery' => 'file_widgetModes.gallery', + 'fe_single_upload' => 'file_widgetModes.fe_single_upload', + 'fe_single_upload_preview' => 'file_widgetModes.fe_single_upload_preview', + 'fe_multiple_upload' => 'file_widgetModes.fe_multiple_upload', + 'fe_multiple_upload_preview' => 'file_widgetModes.fe_multiple_upload_preview', + ], + 'eval' => [ + 'tl_class' => 'clr w50' + ], + 'sql' => 'char(32) NOT NULL default \'normal\'' +]; + +// Load configuration for the frontend editing. +if (\in_array( + MetaModelsContaoFrontendEditingBundle::class, + System::getContainer()->getParameter('kernel.bundles'), + true +)) { + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['metasubselectpalettes']['attr_id']['file']['presentation'][] = + 'fe_template'; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['file_widgetMode']['eval']['submitOnChange'] = true; + + $uploadSettings = [ + 'upload_settings' => [ + 'fe_widget_file_useHomeDir', + 'fe_widget_file_uploadFolder', + 'fe_widget_file_extend_folder', + 'fe_widget_file_normalize_extend_folder', + 'fe_widget_file_doNotOverwrite', + 'fe_widget_file_normalize_filename', + 'fe_widget_file_prefix_filename', + 'fe_widget_file_postfix_filename', + 'fe_widget_file_deselect', + 'fe_widget_file_delete' + ] + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['metasubselectpalettes']['file_widgetMode']['fe_single_upload'] = + $uploadSettings; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['metasubselectpalettes']['file_widgetMode']['fe_single_upload_preview'] = + \array_merge_recursive( + $uploadSettings, + ['upload_settings' => ['fe_widget_file_imageSize']] + ); + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['metasubselectpalettes']['file_widgetMode']['fe_multiple_upload'] = + \array_merge_recursive( + $uploadSettings, + ['upload_settings' => ['fe_widget_file_sortBy']] + ); + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['metasubselectpalettes']['file_widgetMode']['fe_multiple_upload_preview'] = + \array_merge_recursive( + $uploadSettings, + ['upload_settings' => ['fe_widget_file_sortBy', 'fe_widget_file_imageSize']] + ); + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_useHomeDir'] = [ + 'label' => 'fe_widget_file_useHomeDir.label', + 'description' => 'fe_widget_file_useHomeDir.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ + 'tl_class' => 'w50 cbx m12', + ], + 'sql' => "char(1) NOT NULL default ''", + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_uploadFolder'] = [ + 'label' => 'fe_widget_file_uploadFolder.label', + 'description' => 'fe_widget_file_uploadFolder.description', + 'exclude' => true, + 'inputType' => 'fileTree', + 'eval' => [ + 'fieldType' => 'radio', + 'tl_class' => 'w50' + ], + 'sql' => "binary(16) NULL" + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_doNotOverwrite'] = [ + 'label' => 'fe_widget_file_doNotOverwrite.label', + 'description' => 'fe_widget_file_doNotOverwrite.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ + 'tl_class' => 'w50 cbx m12 clr', + ], + 'sql' => "char(1) NOT NULL default ''", + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_deselect'] = [ + 'label' => 'fe_widget_file_deselect.label', + 'description' => 'fe_widget_file_deselect.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ + 'tl_class' => 'w50 clr cbx m12', + ], + 'sql' => "char(1) NOT NULL default ''", + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_delete'] = [ + 'label' => 'fe_widget_file_delete.label', + 'description' => 'fe_widget_file_delete.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ + 'tl_class' => 'w50 cbx m12', + ], + 'sql' => "char(1) NOT NULL default ''", + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_extend_folder'] = [ + 'label' => 'fe_widget_file_extend_folder.label', + 'description' => 'fe_widget_file_extend_folder.description', + 'inputType' => 'text', + 'eval' => [ + 'tl_class' => 'w50 clr' + ], + 'sql' => "longtext" + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_normalize_extend_folder'] = [ + 'label' => 'fe_widget_file_normalize_extend_folder.label', + 'description' => 'fe_widget_file_normalize_extend_folder.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ + 'tl_class' => 'w50 cbx m12', + ], + 'sql' => "char(1) NOT NULL default ''", + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_normalize_filename'] = [ + 'label' => 'fe_widget_file_normalize_filename.label', + 'description' => 'fe_widget_file_normalize_filename.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ + 'tl_class' => 'w50 cbx m12', + ], + 'sql' => "char(1) NOT NULL default ''", + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_prefix_filename'] = [ + 'label' => 'fe_widget_file_prefix_filename.label', + 'description' => 'fe_widget_file_prefix_filename.description', + 'inputType' => 'text', + 'eval' => [ + 'tl_class' => 'w50 clr' + ], + 'sql' => "longtext" + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_postfix_filename'] = [ + 'label' => 'fe_widget_file_postfix_filename.label', + 'description' => 'fe_widget_file_postfix_filename.description', + 'inputType' => 'text', + 'eval' => [ + 'tl_class' => 'w50' + ], + 'sql' => "longtext" + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_sortBy'] = [ + 'label' => 'fe_widget_file_sortBy.label', + 'description' => 'fe_widget_file_sortBy.description', + 'exclude' => true, + 'inputType' => 'select', + 'options' => ['name_asc', 'name_desc', 'date_asc', 'date_desc', 'random'], + 'reference' => [ + 'name_asc' => 'fe_widget_file_sortBy.name_asc', + 'name_desc' => 'fe_widget_file_sortBy.name_desc', + 'date_asc' => 'fe_widget_file_sortBy.date_asc', + 'date_desc' => 'fe_widget_file_sortBy.date_desc', + 'random' => 'fe_widget_file_sortBy.random', + ], + 'sql' => 'varchar(32) NOT NULL default \'\'', + 'eval' => [ + 'tl_class' => 'w50', + 'chosen' => true + ] + ]; + + $GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['fields']['fe_widget_file_imageSize'] = [ + 'label' => 'fe_widget_file_imageSize.label', + 'description' => 'fe_widget_file_imageSize.description', + 'exclude' => true, + 'inputType' => 'imageSize', + 'sql' => 'varchar(255) NOT NULL default \'\'', + 'eval' => [ + 'rgxp' => 'digit', + 'includeBlankOption' => true, + 'nospace' => true, + 'tl_class' => 'w50' + ] + ]; +} diff --git a/src/Resources/contao/dca/tl_metamodel_rendersetting.php b/src/Resources/contao/dca/tl_metamodel_rendersetting.php index 2c15644..8eb74b0 100644 --- a/src/Resources/contao/dca/tl_metamodel_rendersetting.php +++ b/src/Resources/contao/dca/tl_metamodel_rendersetting.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,65 +16,109 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author Stefan Heimes - * @copyright 2012-2019 The MetaModels team. + * @author Cliff Parnitzky + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ +use Contao\BackendUser; +use Contao\System; + /** - * Table tl_metamodel_attribute + * Table tl_metamodel_rendersettings */ $GLOBALS['TL_DCA']['tl_metamodel_rendersetting']['metapalettes']['file extends default'] = [ '+advanced' => ['file_sortBy', 'file_showLink', 'file_showImage'] ]; +$GLOBALS['TL_DCA']['tl_metamodel_rendersetting']['metasubpalettes']['file_showLink'] = [ + 'file_protectedDownload' +]; + $GLOBALS['TL_DCA']['tl_metamodel_rendersetting']['metasubpalettes']['file_showImage'] = [ - 'file_imageSize' + 'file_imageSize', + 'file_placeholder' ]; $GLOBALS['TL_DCA']['tl_metamodel_rendersetting']['fields']['file_sortBy'] = [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_sortBy'], - 'exclude' => true, - 'inputType' => 'select', - 'options' => ['name_asc', 'name_desc', 'date_asc', 'date_desc', 'random'], - 'reference' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting'], - 'sql' => 'varchar(32) NOT NULL default \'\'', - 'eval' => [ + 'label' => 'file_sortBy.label', + 'description' => 'file_sortBy.description', + 'exclude' => true, + 'inputType' => 'select', + 'options' => ['name_asc', 'name_desc', 'date_asc', 'date_desc', 'manual', 'random'], + 'reference' => [ + 'name_asc' => 'file_sortBy.name_asc', + 'name_desc' => 'file_sortBy.name_desc', + 'date_asc' => 'file_sortBy.date_asc', + 'date_desc' => 'file_sortBy.date_desc', + 'random' => 'file_sortBy.random', + 'manual' => 'file_sortBy.manual', + ], + 'sql' => 'varchar(32) NOT NULL default \'\'', + 'eval' => [ 'tl_class' => 'w50', 'chosen' => true ] ]; $GLOBALS['TL_DCA']['tl_metamodel_rendersetting']['fields']['file_showLink'] = [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_showLink'], - 'inputType' => 'checkbox', - 'sql' => 'char(1) NOT NULL default \'\'', - 'eval' => ['tl_class' => 'w50 m12'] + 'label' => 'file_showLink.label', + 'description' => 'file_showLink.description', + 'inputType' => 'checkbox', + 'sql' => 'char(1) NOT NULL default \'\'', + 'eval' => [ + 'submitOnChange' => true, + 'tl_class' => 'clr w50 cbx m12' + ] +]; + +$GLOBALS['TL_DCA']['tl_metamodel_rendersetting']['fields']['file_protectedDownload'] = [ + 'label' => 'file_protectedDownload.label', + 'description' => 'file_protectedDownload.description', + 'inputType' => 'checkbox', + 'sql' => 'char(1) NOT NULL default \'\'', + 'eval' => [ + 'tl_class' => 'w50 cbx m12' + ] ]; $GLOBALS['TL_DCA']['tl_metamodel_rendersetting']['fields']['file_showImage'] = [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_showImage'], - 'inputType' => 'checkbox', - 'sql' => 'char(1) NOT NULL default \'\'', - 'eval' => [ + 'label' => 'file_showImage.label', + 'description' => 'file_showImage.description', + 'inputType' => 'checkbox', + 'sql' => 'char(1) NOT NULL default \'\'', + 'eval' => [ 'submitOnChange' => true, - 'tl_class' => 'clr' + 'tl_class' => 'clr w50 cbx m12' ] ]; $GLOBALS['TL_DCA']['tl_metamodel_rendersetting']['fields']['file_imageSize'] = [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_imageSize'], - 'exclude' => true, - 'inputType' => 'imageSize', - 'options' => $GLOBALS['TL_CROP'], - 'reference' => &$GLOBALS['TL_LANG']['MSC'], - 'sql' => 'varchar(255) NOT NULL default \'\'', - 'eval' => [ - 'rgxp' => 'digit', + 'label' => 'file_imageSize.label', + 'description' => 'file_imageSize.description', + 'exclude' => true, + 'inputType' => 'imageSize', + 'eval' => [ + 'rgxp' => 'natural', 'includeBlankOption' => true, 'nospace' => true, - 'helpwizard' => true, - 'tl_class' => 'w50' + 'tl_class' => 'clr w50' + ], + 'sql' => 'varchar(128) COLLATE ascii_bin NOT NULL default \'\'', +]; + +$GLOBALS['TL_DCA']['tl_metamodel_rendersetting']['fields']['file_placeholder'] = [ + 'label' => 'file_placeholder.label', + 'description' => 'file_placeholder.description', + 'exclude' => true, + 'inputType' => 'fileTree', + 'sql' => 'blob NULL', + 'eval' => [ + 'fieldType' => 'radio', + 'files' => true, + 'filesOnly' => true, + 'tl_class' => 'w50' ] ]; diff --git a/src/Resources/contao/languages/de/tl_metamodel_attribute.php b/src/Resources/contao/languages/de/tl_metamodel_attribute.php deleted file mode 100644 index f322367..0000000 --- a/src/Resources/contao/languages/de/tl_metamodel_attribute.php +++ /dev/null @@ -1,37 +0,0 @@ - - * @author David Molineus - * @author Sven Baumann - * @copyright 2012-2018 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -// Fields. -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['typeOptions']['file'] = 'File'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_customFiletree'][0] = 'Customize the file tree'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_customFiletree'][1] = - 'Allows you to set custom options for the filetree.'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_uploadFolder'][0] = 'Set file root folder'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_uploadFolder'][1] = - 'Selects the root point from which the user will select this file field.'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_validFileTypes'][0] = 'Valid file types'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_validFileTypes'][1] = - 'To overwrite the contao standard file types, please enter a comma separated list of extensions of valid file ' . - 'types for this field.'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_filesOnly'][0] = 'Allow files only'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_filesOnly'][1] = - 'Select this option to restrict the file browser to files only (folders not selectable).'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_multiple'][0] = 'Multiple selection'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['file_multiple'][1] = - 'If selected, user will be able to select more than one item.'; diff --git a/src/Resources/contao/languages/en/tl_metamodel_dcasetting.php b/src/Resources/contao/languages/en/tl_metamodel_dcasetting.php deleted file mode 100644 index 3a71030..0000000 --- a/src/Resources/contao/languages/en/tl_metamodel_dcasetting.php +++ /dev/null @@ -1,29 +0,0 @@ - - * @copyright 2012-2016 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0 - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['file_widgetMode'][0] = 'Widget mode'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['file_widgetMode'][1] = - 'If selected, the files will show as a (sortable) image gallery.'; - -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['file_widgetModes']['normal'] = 'Show selected files only.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['file_widgetModes']['downloads'] = - 'Expand folders as (sortable) file list.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['file_widgetModes']['gallery'] = - 'Expand folders as (sortable) image gallery.'; diff --git a/src/Resources/contao/languages/en/tl_metamodel_rendersetting.php b/src/Resources/contao/languages/en/tl_metamodel_rendersetting.php deleted file mode 100644 index 15bd231..0000000 --- a/src/Resources/contao/languages/en/tl_metamodel_rendersetting.php +++ /dev/null @@ -1,41 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2018 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_showImage'][0] = 'Enable as image field with thumbnail'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_showImage'][1] = - 'If selected, a thumbnail will be created for image files.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_sortBy'][0] = 'Order by'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_sortBy'][1] = 'Please choose the sort order.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_showLink'][0] = - 'Create link as file download or image lightbox'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_showLink'][1] = - 'Wraps the item in a link that will show the fullscreen image or download the file.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_imageSize'][0] = 'Image width and height'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['file_imageSize'][1] = - 'Please enter either the image width, the image height or both measures to resize the image. If you leave both ' . - 'fields blank, the original image size will be displayed.'; - -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['name_asc'] = 'File name (ascending)'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['name_desc'] = 'File name (descending)'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['date_asc'] = 'Date (ascending)'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['date_desc'] = 'Date (descending)'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['random'] = 'Random order'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['manual'] = 'Manual sorting'; diff --git a/src/Resources/contao/languages/et/tl_metamodel_attribute.php b/src/Resources/contao/languages/et/tl_metamodel_attribute.php deleted file mode 100644 index d154fd1..0000000 --- a/src/Resources/contao/languages/et/tl_metamodel_attribute.php +++ /dev/null @@ -1,19 +0,0 @@ -src)): ?> - diff --git a/src/Resources/contao/templates/mm_attr_file.xhtml b/src/Resources/contao/templates/mm_attr_file.xhtml deleted file mode 100644 index 958df13..0000000 --- a/src/Resources/contao/templates/mm_attr_file.xhtml +++ /dev/null @@ -1,30 +0,0 @@ -src)): ?> - - diff --git a/src/Resources/contao/templates/mm_attr_file_alternative.html5 b/src/Resources/contao/templates/mm_attr_file_alternative.html5 index 09d7e1c..136da9c 100644 --- a/src/Resources/contao/templates/mm_attr_file_alternative.html5 +++ b/src/Resources/contao/templates/mm_attr_file_alternative.html5 @@ -1,34 +1,41 @@ src)): ?> -src as $arrFile): ?> -
-settings->get('file_showImage')): ?> -
style=""> - -settings->get('file_showLink')): ?> - settings->get('file_showImage')): ?> - - - - - -settings->get('file_showImage')): ?> - - generateImage($arrFile['src'], $arrFile['caption']) ?> - -settings->get('file_showImage')): ?> - insert('picture_default',$arrFile['picture']); ?> - - - -settings->get('file_showLink')): ?> - - -settings->get('file_showImage')): ?> - -
- -
- -
- + src as $arrFile): ?> +
+ settings->get('file_showImage')): ?> +
style=""> + + + settings->get('file_showLink')): ?> + settings->get('file_showImage')): ?> + + + + + + + settings->get('file_showImage')): ?> + + generateImage($arrFile['src'], $arrFile['caption']) ?> + + settings->get('file_showImage')): ?> + insert('picture_default', $arrFile['picture']); ?> + + + + + settings->get('file_showLink')): ?> + + + + settings->get('file_showImage')): ?> + +
+ +
+ +
+ diff --git a/src/Resources/contao/templates/mm_attr_file_alternative.text b/src/Resources/contao/templates/mm_attr_file_alternative.text index e9bad88..9c95fd1 100644 --- a/src/Resources/contao/templates/mm_attr_file_alternative.text +++ b/src/Resources/contao/templates/mm_attr_file_alternative.text @@ -1,3 +1,3 @@ -raw['path'] as $file): ?> - - \ No newline at end of file +files as $file): ?> + + diff --git a/src/Resources/contao/templates/mm_attr_file_alternative.xhtml b/src/Resources/contao/templates/mm_attr_file_alternative.xhtml deleted file mode 100644 index ba0c4b1..0000000 --- a/src/Resources/contao/templates/mm_attr_file_alternative.xhtml +++ /dev/null @@ -1,23 +0,0 @@ -src)): ?> -src as $arrFile): ?> -

-settings->get('file_showLink')): ?> - settings->get('file_showImage')): ?> - - - - - -settings->get('file_showImage')): ?> - - generateImage($arrFile['src'], $arrFile['caption']) ?> - - - - -settings->get('file_showLink')): ?> - - -

- - diff --git a/src/Resources/translations/tl_metamodel_attribute.de.xlf b/src/Resources/translations/tl_metamodel_attribute.de.xlf new file mode 100644 index 0000000..4f0a741 --- /dev/null +++ b/src/Resources/translations/tl_metamodel_attribute.de.xlf @@ -0,0 +1,62 @@ + + + + + File + Datei + + + Customize the file tree + Passen Sie den Dateibaum an + + + Allows you to set custom options for the filetree. + Erlaubt Ihnen, individuelle Optionen für den Dateibaum zu setzen. + + + Set file root folder + Wurzelordner (Root) angeben + + + Selects the root point from which the user will select this file field. + Wählen Sie den Punkt im Dateibaum, von dem aus Benutzer Daten hinzufügen können. + + + Valid file types + Gültige Dateitypen + + + To overwrite the contao standard file types, please enter a comma separated list of extensions of valid file types for this field. + Geben Sie hier eine kommagetrennte Liste gültiger Dateiendungen ein, um die Standard-Dateitypen von Contao zu überschreiben. + + + Allowed types for selection + Erlaubte Dateitypen + + + Select a restriction on files or folders - default is no restriction on files or folders. + Wählen Sie eine Einschränkung für Dateien oder Ordner aus - Standard ist keine Einschränkung für Dateien oder Ordner. + + + Multiple selection + Mehrfachauswahl + + + If selected, user will be able to select more than one item. + Wenn aktiviert, können mehrere Dateien ausgewählt werden. + + + Allow files and folders + Erlauben von Dateien und Ordnern + + + Allow files only + Nur Dateien erlauben + + + Allow folders only + Nur Ordner erlauben + + + + \ No newline at end of file diff --git a/src/Resources/translations/tl_metamodel_attribute.en.xlf b/src/Resources/translations/tl_metamodel_attribute.en.xlf new file mode 100644 index 0000000..539adb2 --- /dev/null +++ b/src/Resources/translations/tl_metamodel_attribute.en.xlf @@ -0,0 +1,49 @@ + + + + + + File + + + Customize the file tree + + + Allows you to set custom options for the filetree. + + + Set file root folder + + + Selects the root point from which the user will select this file field. + + + Valid file types + + + To overwrite the contao standard file types, please enter a comma separated list of extensions of valid file types for this field. + + + Allowed types for selection + + + Select a restriction on files or folders - default is no restriction on files or folders. + + + Multiple selection + + + If selected, user will be able to select more than one item. + + + Allow files and folders + + + Allow files only + + + Allow folders only + + + + diff --git a/src/Resources/translations/tl_metamodel_attribute.fr.xlf b/src/Resources/translations/tl_metamodel_attribute.fr.xlf new file mode 100644 index 0000000..26f346a --- /dev/null +++ b/src/Resources/translations/tl_metamodel_attribute.fr.xlf @@ -0,0 +1,62 @@ + + + + + File + Fichier + + + Customize the file tree + Personnaliser l'arborescence + + + Allows you to set custom options for the filetree. + Permet de personnaliser les options de l'arborescence. + + + Set file root folder + Spécifier le dossier racine + + + Selects the root point from which the user will select this file field. + Sélectionne le point racine à partir duquel l'utilisateur sélectionnera ce champ de fichier. + + + Valid file types + Types de fichiers autorisés + + + To overwrite the contao standard file types, please enter a comma separated list of extensions of valid file types for this field. + Saisir une liste d'extensions de fichiers séparées par des virgules pour remplacer les types standards définis dans Contao. + + + Allowed types for selection + Types autorisés à la sélection + + + Select a restriction on files or folders - default is no restriction on files or folders. + Définir une restriction de fichiers ou de dossiers. Par défaut, pas de restriction. + + + Multiple selection + Sélection multiple + + + If selected, user will be able to select more than one item. + Si coché, l'utilisateur pourra sélectionner plusieurs éléments. + + + Allow files and folders + Autoriser fichiers et dossiers + + + Allow files only + Autoriser seulement les fichiers + + + Allow folders only + Autoriser seulement les dossiers + + + + \ No newline at end of file diff --git a/src/Resources/translations/tl_metamodel_dcasetting.de.xlf b/src/Resources/translations/tl_metamodel_dcasetting.de.xlf new file mode 100644 index 0000000..1db9d43 --- /dev/null +++ b/src/Resources/translations/tl_metamodel_dcasetting.de.xlf @@ -0,0 +1,162 @@ + + + + + File upload settings + Einstellungen zum Upload von Dateien + + + Widget mode + Widget-Modus + + + With the mode the display type can be selected. + Mit dem Modus kann die Anzeigeart ausgewählt werden. + + + Preserve existing files + Vorhandene Dateien beibehalten + + + Add a numeric suffix to the new file, if the file name already exists. + Fügen Sie der neuen Datei ein numerisches Suffix hinzu, wenn der Dateiname bereits existiert. + + + Use home directory + Home-Verzeichnis verwenden + + + If this option is active, store the file in the home directory if there is an authenticated user. If the target folder configured too and the user is authenticated, so this folder is the base upload folder. + Wenn diese Option aktiv ist, wird die Datei im Home-Verzeichnis gespeichert, wenn es einen authentifizierten Benutzer gibt. Ist der Zielordner zu konfiguriert und der Benutzer ist authentifiziert, so ist dieser Ordner der Basis-Upload-Ordner. + + + Target folder + Zielordner + + + Please select the target folder from the files directory. If the home dir configured too and is not authenticated a user, so this folder is the base folder. + Bitte wählen Sie den Zielordner aus dem Dateiverzeichnis aus. Ist das Home-Verzeichnis konfiguriert und ein Benutzer nicht authentifiziert, so ist dieser Ordner der Basisordner. + + + Extend folder + Ordner erweitern + + + Here you can extend the base upload folder path. The Contao insert tags are supported to extend the path. + Hier können Sie den Basis-Upload-Ordnerpfad erweitern. Die Contao Insert-Tags werden unterstützt, um den Pfad zu erweitern. + + + Deselect file + Datei abwählen + + + If this option deselect file is active, then that file entry is remove from this model. + Wenn die Option Datei abwählen aktiviert ist, wird dieser Dateieintrag aus diesem Modell entfernt. + + + Delete file + Datei löschen + + + If this option delete file is active, then that file entry is remove from this model and from the file directory. + Ist die Option Datei löschen aktiv, so wird dieser Dateieintrag aus diesem Modell und aus dem Dateiverzeichnis entfernt. + + + Normalize extend folder + Erweiterten Ordner normalisieren + + + Normalize the extend folder with an alias generator from the string util. + Normalisieren Sie den erweiterten Ordner mit einem Alias-Generator. + + + Normalize filename + Dateinamen normalisieren + + + Normalize the filename with an alias generator from the string util. + Normalisieren Sie den Dateinamen mit einem Alias-Generator. + + + Prefix filename + Dateinamen Präfix + + + Here you can prefix the filename. The Contao insert tags are supported to prefix the filename. + Hier können Sie Zeichen den Dateinamen vorangestellt werden. Die Contao-Insert-Tags werden für das Voranstellen des Dateinamens unterstützt. + + + Postfix filename + Dateinamen Postfix + + + Here you can postfix the filename. The Contao insert tags are supported to postfix the filename. + Hier können Sie Zeichen den Dateinamen anhängen. Die Contao-Insert-Tags werden für das Voranstellen des Dateinamens unterstützt. + + + Thumbnail width and height + Breite und Höhe der Vorschaubilder + + + Please enter either the image width, the image height or both measures to resize the image. If you leave both fields blank, the original image size will be displayed. + Bitte geben Sie entweder die Bildbreite, die Bildhöhe oder beides ein, damit das Bild skaliert wird. Falls Sie beide Felder leer lassen, wird das Bild in Originalgröße ausgegeben. + + + Order by + Sortieren nach + + + Please choose the sort order. + Bitte wählen Sie die Sortierreihenfolge. + + + Show files as list. + Dateien als Liste anzeigen. + + + Show files as sortable file list e.g. for downloads. + Dateien als sortierbare Dateiliste anzeigen z.B. für Downloads. + + + Show files as sortable images e.g. for gallery. + Dateien als sortierbare Bilder anzeigen z.B. für eine Galerie. + + + Single file upload [only for frontend editing] + Einzelner Datei-Upload [nur für Frontend-Bearbeitung] + + + Single file upload with show thumbnail [only for frontend editing] + Einzelner Datei-Upload mit Anzeige der Vorschaubilder [nur für Frontend-Bearbeitung] + + + Multiple file upload [only for frontend editing] + Mehrfacher Datei-Upload [nur für Frontend-Bearbeitung] + + + Multiple file upload with show thumbnail [only for frontend editing] + Mehrfacher Datei-Upload mit Anzeige der Vorschaubilder [nur für Frontend-Bearbeitung] + + + File name (ascending) + Dateiname (aufsteigend) + + + File name (descending) + Dateiname (absteigend) + + + Date (ascending) + Datum (aufsteigend) + + + Date (descending) + Datum (absteigend) + + + Random order + Zufällige Reihenfolge + + + + diff --git a/src/Resources/translations/tl_metamodel_dcasetting.en.xlf b/src/Resources/translations/tl_metamodel_dcasetting.en.xlf new file mode 100644 index 0000000..51df50a --- /dev/null +++ b/src/Resources/translations/tl_metamodel_dcasetting.en.xlf @@ -0,0 +1,124 @@ + + + + + + File upload settings + + + Widget mode + + + With the mode the display type can be selected. + + + Preserve existing files + + + Add a numeric suffix to the new file, if the file name already exists. + + + Use home directory + + + If this option is active, store the file in the home directory if there is an authenticated user. If the target folder configured too and the user is authenticated, so this folder is the base upload folder. + + + Target folder + + + Please select the target folder from the files directory. If the home dir configured too and is not authenticated a user, so this folder is the base folder. + + + Extend folder + + + Here you can extend the base upload folder path. The Contao insert tags are supported to extend the path. + + + Deselect file + + + If this option deselect file is active, then that file entry is remove from this model. + + + Delete file + + + If this option delete file is active, then that file entry is remove from this model and from the file directory. + + + Normalize extend folder + + + Normalize the extend folder with an alias generator from the string util. + + + Normalize filename + + + Normalize the filename with an alias generator from the string util. + + + Prefix filename + + + Here you can prefix the filename. The Contao insert tags are supported to prefix the filename. + + + Postfix filename + + + Here you can postfix the filename. The Contao insert tags are supported to postfix the filename. + + + Thumbnail width and height + + + Please enter either the image width, the image height or both measures to resize the image. If you leave both fields blank, the original image size will be displayed. + + + Order by + + + Please choose the sort order. + + + Show files as list. + + + Show files as sortable file list e.g. for downloads. + + + Show files as sortable images e.g. for gallery. + + + Single file upload [only for frontend editing] + + + Single file upload with show thumbnail [only for frontend editing] + + + Multiple file upload [only for frontend editing] + + + Multiple file upload with show thumbnail [only for frontend editing] + + + File name (ascending) + + + File name (descending) + + + Date (ascending) + + + Date (descending) + + + Random order + + + + diff --git a/src/Resources/translations/tl_metamodel_dcasetting.fr.xlf b/src/Resources/translations/tl_metamodel_dcasetting.fr.xlf new file mode 100644 index 0000000..99f6282 --- /dev/null +++ b/src/Resources/translations/tl_metamodel_dcasetting.fr.xlf @@ -0,0 +1,162 @@ + + + + + File upload settings + Paramètres de transfert de fichiers + + + Widget mode + Mode du widget + + + With the mode the display type can be selected. + Le mode permet de sélectionner le type d'affichage. + + + Preserve existing files + Préserver les fichiers existants + + + Add a numeric suffix to the new file, if the file name already exists. + Ajouter un suffixe numérique au nouveau fichier si le nom de fichier existe déjà. + + + Use home directory + Utiliser le répertoire personnel + + + If this option is active, store the file in the home directory if there is an authenticated user. If the target folder configured too and the user is authenticated, so this folder is the base upload folder. + Enregistrer le fichier dans le dossier personnel de l'utilisateur s'il s'est authentifié. Si le dossier cible est également configuré et que l'utilisateur est authentifié, ce dossier est le dossier de téléchargement de base. + + + Target folder + Dossier cible + + + Please select the target folder from the files directory. If the home dir configured too and is not authenticated a user, so this folder is the base folder. + Sélectionner un dossier cible dans le répertoire des fichiers. Si un répertoire personnel est configuré mais que l'utilisateur n'est pas authentifié, ce dossier sera le dossier de base. + + + Extend folder + Étendre le chemin du dossier + + + Here you can extend the base upload folder path. The Contao insert tags are supported to extend the path. + Ici vous pouvez étendre le chemin du dossier de téléchargement de base. Les insert tags de Contao sont supportés. + + + Deselect file + Désélectionner le fichier + + + If this option deselect file is active, then that file entry is remove from this model. + Si cette option est active, cette entrée de fichier sera supprimée de ce modèle. + + + Delete file + Supprimer le fichier + + + If this option delete file is active, then that file entry is remove from this model and from the file directory. + Si cette option est active, cette entrée de fichier sera supprimée de ce modèle et du dossier. + + + Normalize extend folder + Normaliser l'extension du chemin du dossier + + + Normalize the extend folder with an alias generator from the string util. + Normaliser l'extension de dossier avec un générateur d'alias à partir de la chaîne util. + + + Normalize filename + Normaliser le nom de fichier. + + + Normalize the filename with an alias generator from the string util. + Normaliser le nom de fichier avec un générateur d'alias à partir de la chaîne util. + + + Prefix filename + Préfixe de nom de fichier + + + Here you can prefix the filename. The Contao insert tags are supported to prefix the filename. + Ajouter un préfixe aux noms de fichiers. Les insert tags de Contao peuvent être utilisés. + + + Postfix filename + Suffixe de nom de fichier + + + Here you can postfix the filename. The Contao insert tags are supported to postfix the filename. + Ajouter un postfixe aux noms de fichiers. Les insert tags de Contao peuvent être utilisés. + + + Thumbnail width and height + Hauteur et largeur de la vignette + + + Please enter either the image width, the image height or both measures to resize the image. If you leave both fields blank, the original image size will be displayed. + Indiquer la largeur de l'image, sa hauteur ou les deux pour la redimensionner. Si les deux champs sont vides, l'image s'affichera à sa taille originale. + + + Order by + Classer + + + Please choose the sort order. + Sélectionner l'ordre de classement. + + + Show files as list. + Afficher les fichiers en liste + + + Show files as sortable file list e.g. for downloads. + Afficher les fichiers sous forme de liste à classer (ex : pour des téléchargements) + + + Show files as sortable images e.g. for gallery. + Afficher les fichiers sous forme d'images à classer (ex : pour une galerie) + + + Single file upload [only for frontend editing] + Télécharger un fichier unique [réservé à l'édition en frontend] + + + Single file upload with show thumbnail [only for frontend editing] + Télécharger un fichier unique avec la vignette [réservé à l'édition en frontend] + + + Multiple file upload [only for frontend editing] + Téléchargment multiple [pour l'édition en frontend] + + + Multiple file upload with show thumbnail [only for frontend editing] + Téléchargment multiple avec vignette [pour l'édition en frontend] + + + File name (ascending) + Nom de fichier (ascendant) + + + File name (descending) + Nom de fichier (descendant) + + + Date (ascending) + Date (ascendante) + + + Date (descending) + Date (descendante) + + + Random order + Ordre aléatoire + + + + \ No newline at end of file diff --git a/src/Resources/translations/tl_metamodel_rendersetting.de.xlf b/src/Resources/translations/tl_metamodel_rendersetting.de.xlf new file mode 100644 index 0000000..0c45624 --- /dev/null +++ b/src/Resources/translations/tl_metamodel_rendersetting.de.xlf @@ -0,0 +1,78 @@ + + + + + Enable as image field with thumbnail + Als Bildfeld mit Vorschaubild benutzen + + + If selected, a thumbnail will be created for image files. + Falls aktiviert, wird für das Bild ein verkleinertes Vorschaubild generiert. + + + Order by + Sortierung nach + + + Please choose the sort order. + Bitte wählen Sie die Sortierreihenfolge. + + + Create link as file download or image lightbox + Link als Download oder Lightbox erstellen + + + Wraps the item in a link that will show the fullscreen image or download the file. + Das Item wird in einen Link eingebettet, der entweder für eine Großansicht oder einen Download dienen kann. + + + Image width and height + Bildbreite und -höhe + + + Please enter either the image width, the image height or both measures to resize the image. If you leave both fields blank, the original image size will be displayed. + Bitte geben Sie entweder die Bildbreite, die Bildhöhe oder beides ein, damit das Bild skaliert wird. Falls Sie beide Felder leer lassen, wird das Bild in Originalgröße ausgegeben. + + + Image placeholder + Bild als Platzhalter + + + Select an image as a placeholder if no image is selected. + Wählen Sie ein Bild als Platzhalter, wenn kein Bild ausgewählt ist. + + + Protected download + Geschützter Download + + + If this option is selected, the URL is only temporarily valid. + Wenn diese Option ausgewählt ist, ist die URL nur vorübergehend gültig. + + + File name (ascending) + Dateiname (aufsteigend) + + + File name (descending) + Dateiname (absteigend) + + + Date (ascending) + Datum (aufsteigend) + + + Date (descending) + Datum (absteigend) + + + Random order + Zufällige Reihenfolge + + + Manual sorting + Manuelle Sortierung + + + + \ No newline at end of file diff --git a/src/Resources/translations/tl_metamodel_rendersetting.en.xlf b/src/Resources/translations/tl_metamodel_rendersetting.en.xlf new file mode 100644 index 0000000..93ad4b4 --- /dev/null +++ b/src/Resources/translations/tl_metamodel_rendersetting.en.xlf @@ -0,0 +1,61 @@ + + + + + + Enable as image field with thumbnail + + + If selected, a thumbnail will be created for image files. + + + Order by + + + Please choose the sort order. + + + Create link as file download or image lightbox + + + Wraps the item in a link that will show the fullscreen image or download the file. + + + Image width and height + + + Please enter either the image width, the image height or both measures to resize the image. If you leave both fields blank, the original image size will be displayed. + + + Image placeholder + + + Select an image as a placeholder if no image is selected. + + + Protected download + + + If this option is selected, the URL is only temporarily valid. + + + File name (ascending) + + + File name (descending) + + + Date (ascending) + + + Date (descending) + + + Random order + + + Manual sorting + + + + diff --git a/src/Resources/translations/tl_metamodel_rendersetting.fr.xlf b/src/Resources/translations/tl_metamodel_rendersetting.fr.xlf new file mode 100644 index 0000000..203eb64 --- /dev/null +++ b/src/Resources/translations/tl_metamodel_rendersetting.fr.xlf @@ -0,0 +1,78 @@ + + + + + Enable as image field with thumbnail + Gérer comme image avec vignette + + + If selected, a thumbnail will be created for image files. + Cocher pour créer une vignette pour les fichiers image. + + + Order by + Classer par + + + Please choose the sort order. + Choisir l'ordre de classement. + + + Create link as file download or image lightbox + Créer un lien de téléchargement ou une image lightbox. + + + Wraps the item in a link that will show the fullscreen image or download the file. + Enveloppe l'élément dans un lien qui affichera l'image en plein écran ou téléchargera le fichier. + + + Image width and height + Largeur et hauteur de l'image + + + Please enter either the image width, the image height or both measures to resize the image. If you leave both fields blank, the original image size will be displayed. + Indiquer la largeur, la hauteur ou les deux pour redimensionner l'image. Si les deux sont vides, l'image sera affichée en taille originale. + + + Image placeholder + Image de remplacement + + + Select an image as a placeholder if no image is selected. + Sélectionner une image de remplacement tant qu'aucune image n'est sélectionnée. + + + Protected download + Téléchargement sécurisé + + + If this option is selected, the URL is only temporarily valid. + Si l'option est sélectionnée, l'url sera temporaire. + + + File name (ascending) + Nom de fichier (ascendant) + + + File name (descending) + Nom de fichier (descendant) + + + Date (ascending) + Date (ascendante) + + + Date (descending) + Date (descendante) + + + Random order + Ordre aléatoire + + + Manual sorting + Ordre manuel + + + + \ No newline at end of file diff --git a/src/Schema/DoctrineSchemaGenerator.php b/src/Schema/DoctrineSchemaGenerator.php new file mode 100644 index 0000000..fb045a6 --- /dev/null +++ b/src/Schema/DoctrineSchemaGenerator.php @@ -0,0 +1,60 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2022 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\AttributeFileBundle\Schema; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\Types; +use MetaModels\Information\AttributeInformation; +use MetaModels\Schema\Doctrine\AbstractAttributeTypeSchemaGenerator; + +/** + * This adds all alias columns to doctrine tables schemas. + */ +class DoctrineSchemaGenerator extends AbstractAttributeTypeSchemaGenerator +{ + /** + * {@inheritDoc} + */ + protected function getTypeName(): string + { + return 'file'; + } + + /** + * {@inheritDoc} + */ + protected function generateAttribute(Table $tableSchema, AttributeInformation $attribute): void + { + $this->setColumnData($tableSchema, $attribute->getName(), Types::BLOB, [ + 'notnull' => false, + 'length' => 65535, + ]); + + if ($attribute->getConfigurationValue('file_multiple')) { + $this->setColumnData($tableSchema, $attribute->getName() . '__sort', Types::BLOB, [ + 'notnull' => false, + 'length' => 65535, + ]); + } + } +} diff --git a/src/deprecated-autoload.php b/src/deprecated-autoload.php index 29ceb5d..7a050dc 100644 --- a/src/deprecated-autoload.php +++ b/src/deprecated-autoload.php @@ -23,9 +23,7 @@ use MetaModels\AttributeFileBundle\Attribute\File; use MetaModels\AttributeFileBundle\Attribute\FileOrder; use MetaModels\AttributeFileBundle\DcGeneral\AttributeFileDefinition; -use MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsListener; -use MetaModels\AttributeFileBundle\Helper\UpgradeHandler; -use MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute\BaseListener; +use MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsProvider; // This hack is to load the "old locations" of the classes. spl_autoload_register( @@ -34,9 +32,8 @@ function ($class) { 'MetaModels\Attribute\File\File' => File::class, 'MetaModels\Attribute\File\FileOrder' => FileOrder::class, 'MetaModels\Attribute\File\AttributeTypeFactory' => AttributeTypeFactory::class, - 'MetaModels\Attribute\File\Helper\UpgradeHandler' => UpgradeHandler::class, 'MetaModels\DcGeneral\AttributeFileDefinition' => AttributeFileDefinition::class, - 'MetaModels\Events\Attribute\File\ImageSizeOptions' => ImageSizeOptionsListener::class + 'MetaModels\Events\Attribute\File\ImageSizeOptions' => ImageSizeOptionsProvider::class ]; if (isset($classes[$class])) { diff --git a/tests/Attribute/FileAttributeTypeFactoryTest.php b/tests/Attribute/FileAttributeTypeFactoryTest.php index 9ca637c..a5d1bd1 100644 --- a/tests/Attribute/FileAttributeTypeFactoryTest.php +++ b/tests/Attribute/FileAttributeTypeFactoryTest.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2021 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package MetaModels/attribute_file * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2021 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,6 +32,7 @@ use MetaModels\Helper\TableManipulator; use MetaModels\Helper\ToolboxFile; use MetaModels\IMetaModel; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; /** @@ -46,9 +47,7 @@ class FileAttributeTypeFactoryTest extends TestCase * Mock a MetaModel. * * @param string $tableName The table name. - * * @param string $language The language. - * * @param string $fallbackLanguage The fallback language. * * @return IMetaModel @@ -58,19 +57,19 @@ protected function mockMetaModel($tableName, $language, $fallbackLanguage) $metaModel = $this->getMockForAbstractClass(IMetaModel::class); $metaModel - ->expects($this->any()) + ->expects(self::any()) ->method('getTableName') - ->will($this->returnValue($tableName)); + ->willReturn($tableName); $metaModel - ->expects($this->any()) + ->expects(self::any()) ->method('getActiveLanguage') - ->will($this->returnValue($language)); + ->willReturn($language); $metaModel - ->expects($this->any()) + ->expects(self::any()) ->method('getFallbackLanguage') - ->will($this->returnValue($fallbackLanguage)); + ->willReturn($fallbackLanguage); return $metaModel; } @@ -78,7 +77,7 @@ protected function mockMetaModel($tableName, $language, $fallbackLanguage) /** * Mock the database connection. * - * @return \PHPUnit_Framework_MockObject_MockObject|Connection + * @return MockObject|Connection */ private function mockConnection(AbstractSchemaManager $schemaManager = null) { @@ -87,9 +86,9 @@ private function mockConnection(AbstractSchemaManager $schemaManager = null) ->getMock(); $connection - ->expects($this->any()) - ->method('getSchemaManager') - ->willReturn($schemaManager); + ->expects(self::any()) + ->method('createSchemaManager') + ->willReturn($schemaManager ?? $this->mockSchemaManager()); return $connection; } @@ -107,14 +106,12 @@ private function mockSchemaManager(array $tableSchema = []) ); $manager - ->expects($this->any()) + ->expects(self::any()) ->method('listTableColumns') - ->will( - $this->returnCallback( - function ($table) use ($tableSchema) { - return $tableSchema[$table] ?? null; - } - ) + ->willReturnCallback( + function ($table) use ($tableSchema) { + return $tableSchema[$table] ?? null; + } ); return $manager; @@ -125,7 +122,7 @@ function ($table) use ($tableSchema) { * * @param Connection $connection The database connection mock. * - * @return TableManipulator|\PHPUnit_Framework_MockObject_MockObject + * @return TableManipulator|MockObject */ private function mockTableManipulator(Connection $connection) { @@ -212,10 +209,10 @@ public function testCreateFile() $this->mockMetaModel('mm_test', 'de', 'en') ); - $this->assertInstanceOf(File::class, $attribute); + self::assertInstanceOf(File::class, $attribute); foreach ($values as $key => $value) { - $this->assertEquals($value, $attribute->get($key), $key); + self::assertEquals($value, $attribute->get($key), $key); } } @@ -244,6 +241,6 @@ public function testCreateOrderSelect() $this->mockMetaModel('mm_test', 'de', 'en') ); - $this->assertInstanceOf(FileOrder::class, $attribute); + self::assertInstanceOf(FileOrder::class, $attribute); } } diff --git a/tests/Attribute/FileTest.php b/tests/Attribute/FileTest.php index 913db99..d434ff5 100644 --- a/tests/Attribute/FileTest.php +++ b/tests/Attribute/FileTest.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author David Greminger * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,21 +24,25 @@ use Contao\CoreBundle\Framework\Adapter; use Contao\CoreBundle\Image\ImageFactoryInterface; +use Contao\StringUtil; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Platforms\AbstractPlatform; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; use Doctrine\DBAL\Query\QueryBuilder; -use Doctrine\DBAL\Statement; +use Doctrine\DBAL\Result; use MetaModels\AttributeFileBundle\Attribute\File; use MetaModels\Helper\TableManipulator; use MetaModels\Helper\ToolboxFile; use MetaModels\IMetaModel; +use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; /** * Unit tests to test class File. * * @covers \MetaModels\AttributeFileBundle\Attribute\File + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FileTest extends TestCase { @@ -46,24 +50,23 @@ class FileTest extends TestCase * Mock a MetaModel. * * @param string $tableName The table name. - * * @param string $language The language. * * @return IMetaModel */ protected function mockMetaModel($tableName, $language) { - $metaModel = $this->getMockForAbstractClass('MetaModels\IMetaModel'); + $metaModel = $this->getMockForAbstractClass(IMetaModel::class); $metaModel - ->expects($this->any()) + ->expects(self::any()) ->method('getTableName') - ->will($this->returnValue($tableName)); + ->willReturn($tableName); $metaModel - ->expects($this->any()) + ->expects(self::any()) ->method('getActiveLanguage') - ->will($this->returnValue($language)); + ->willReturn($language); return $metaModel; } @@ -73,14 +76,23 @@ protected function mockMetaModel($tableName, $language) * * @param array $methods The method names to mock. * - * @return \PHPUnit_Framework_MockObject_MockObject|Connection + * @return MockObject|Connection */ private function mockConnection($methods = []) { - return $this->getMockBuilder(Connection::class) + $connection = $this->getMockBuilder(Connection::class) ->disableOriginalConstructor() - ->setMethods($methods) + ->onlyMethods(\array_merge($methods, ['getDatabasePlatform'])) ->getMock(); + + $platform = $this + ->getMockBuilder(AbstractPlatform::class) + ->disableOriginalConstructor() + ->onlyMethods([]) + ->getMockForAbstractClass(); + $connection->method('getDatabasePlatform')->willReturn($platform); + + return $connection; } /** @@ -88,7 +100,7 @@ private function mockConnection($methods = []) * * @param Connection $connection The database connection mock. * - * @return TableManipulator|\PHPUnit_Framework_MockObject_MockObject + * @return TableManipulator|MockObject */ private function mockTableManipulator(Connection $connection) { @@ -100,7 +112,9 @@ private function mockTableManipulator(Connection $connection) /** * Mock the image factory. * - * @return ImageFactoryInterface|\PHPUnit_Framework_MockObject_MockObject + * @return ImageFactoryInterface|MockObject + * + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) */ private function mockImageFactory() { @@ -173,7 +187,7 @@ public function testInstantiation() $this->mockConfig() ); - $this->assertInstanceOf('MetaModels\AttributeFileBundle\Attribute\File', $file); + self::assertInstanceOf(File::class, $file); } /** @@ -199,62 +213,192 @@ public function testEmptyValues() $this->mockConfig() ); - $this->assertEquals( + self::assertEquals( ['bin' => [], 'value' => [], 'path' => [], 'meta' => []], $file->widgetToValue(null, 1) ); - $this->assertEquals( + self::assertEquals( ['bin' => [], 'value' => [], 'path' => [], 'meta' => []], - $file->widgetToValue(array(), 1) + $file->widgetToValue([], 1) ); } /** - * Test the search for method. - * - * @return void + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - public function testSearchFor() + public function testSearchForFileName() { - $metaModel = $this->mockMetaModel('mm_test', 'en'); - $connection = $this->mockConnection(['createQueryBuilder', 'getDatabasePlatform']); - $manipulator = $this->mockTableManipulator($connection); + $metaModel = $this->mockMetaModel('mm_test', 'en'); + $connection = $this->mockConnection(['createQueryBuilder']); + $manipulator = $this->mockTableManipulator($connection); + + $result1 = $this + ->getMockBuilder(Result::class) + ->disableOriginalConstructor() + ->onlyMethods(['fetchAllAssociative']) + ->getMock(); + $result1 + ->expects(self::once()) + ->method('fetchAllAssociative') + ->willReturn( + [ + [ + 'pid' => StringUtil::uuidToBin('b4a3201a-bef2-153c-85ae-66930f01feda'), + 'uuid' => StringUtil::uuidToBin('e68feb56-339b-1eb2-a675-7a5107362e40'), + ], + [ + 'pid' => StringUtil::uuidToBin('b4a3201a-bef2-153c-85ae-66930f01feda'), + 'uuid' => StringUtil::uuidToBin('6e38171a-47c3-1e91-83b4-b759ede063be'), + ], + [ + 'pid' => StringUtil::uuidToBin('314f23ae-30ce-11bb-bbd3-2009656507f7'), + 'uuid' => StringUtil::uuidToBin('0e9e4236-2468-1bfa-89f8-ca45602bec2a'), + ], + ] + ); + + $builder1 = $this + ->getMockBuilder(QueryBuilder::class) + ->setConstructorArgs([$connection]) + ->onlyMethods(['executeQuery', 'expr']) + ->getMock(); + + $builder1->expects(self::once())->method('expr')->willReturn(new ExpressionBuilder($connection)); + $builder1 + ->expects(self::once()) + ->method('executeQuery') + ->willReturn($result1); + + $result2 = $this + ->getMockBuilder(Result::class) + ->disableOriginalConstructor() + ->onlyMethods(['fetchFirstColumn']) + ->getMock(); + $result2 + ->expects(self::once()) + ->method('fetchFirstColumn') + ->willReturn([1, 2, 3, 4, 5]); + + $builder2 = $this + ->getMockBuilder(QueryBuilder::class) + ->setConstructorArgs([$connection]) + ->onlyMethods(['executeQuery']) + ->getMock(); + + $builder2 + ->expects(self::once()) + ->method('executeQuery') + ->willReturn($result2); - $platform = $this->getMockForAbstractClass(AbstractPlatform::class); $connection - ->expects($this->once()) - ->method('getDatabasePlatform') - ->willReturn($platform); + ->expects(self::exactly(2)) + ->method('createQueryBuilder') + ->willReturnOnConsecutiveCalls($builder1, $builder2); + + $file = new File( + $metaModel, + [ + 'colname' => 'file_attribute', + 'file_multiple' => false + ], + $connection, + $manipulator, + $this->mockToolboxFile(), + $this->mockStringUtil(), + $this->mockValidator(), + $this->mockFileRepository(), + $this->mockConfig() + ); + + self::assertSame(['1', '2', '3', '4', '5'], $file->searchFor('*test?value')); - $statement = $this - ->getMockBuilder(Statement::class) + self::assertSame( + 'SELECT f.uuid, f.pid FROM tl_files f WHERE f.name LIKE :value', + $builder1->getSQL() + ); + self::assertSame(['value' => '%test_value'], $builder1->getParameters()); + + self::assertSame( + 'SELECT t.id FROM mm_test t WHERE ' . + '(t.file_attribute LIKE :value_0)' . + ' OR (t.file_attribute LIKE :value_1)' . + ' OR (t.file_attribute LIKE :value_2)' . + ' OR (t.file_attribute LIKE :value_3)' . + ' OR (t.file_attribute LIKE :value_4)', + $builder2->getSQL() + ); + self::assertSame( + [ + 'value_0' => '%' . StringUtil::uuidToBin('b4a3201a-bef2-153c-85ae-66930f01feda') . '%', + 'value_1' => '%' . StringUtil::uuidToBin('e68feb56-339b-1eb2-a675-7a5107362e40') . '%', + 'value_2' => '%' . StringUtil::uuidToBin('6e38171a-47c3-1e91-83b4-b759ede063be') . '%', + 'value_3' => '%' . StringUtil::uuidToBin('314f23ae-30ce-11bb-bbd3-2009656507f7') . '%', + 'value_4' => '%' . StringUtil::uuidToBin('0e9e4236-2468-1bfa-89f8-ca45602bec2a') . '%', + ], + $builder2->getParameters() + ); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testSearchForUuid() + { + $metaModel = $this->mockMetaModel('mm_test', 'en'); + $connection = $this->mockConnection(['createQueryBuilder']); + $manipulator = $this->mockTableManipulator($connection); + + $result1 = $this + ->getMockBuilder(Result::class) ->disableOriginalConstructor() - ->setMethods(['fetchAll']) + ->onlyMethods(['fetchAllAssociative']) ->getMock(); - $statement - ->expects($this->once()) - ->method('fetchAll') - ->with(\PDO::FETCH_COLUMN) - ->willReturn(['1', '2', '3', '4', '5']); + $result1 + ->expects(self::once()) + ->method('fetchAllAssociative') + ->willReturn( + [ + [ + 'pid' => StringUtil::uuidToBin('b4a3201a-bef2-153c-85ae-66930f01feda'), + 'uuid' => StringUtil::uuidToBin('e68feb56-339b-1eb2-a675-7a5107362e40'), + ], + ] + ); $builder1 = $this ->getMockBuilder(QueryBuilder::class) - ->disableOriginalConstructor() - ->setMethods(['expr']) + ->setConstructorArgs([$connection]) + ->onlyMethods(['executeQuery']) ->getMock(); - $builder1->expects($this->once())->method('expr')->willReturn(new ExpressionBuilder($connection)); + $builder1 + ->expects(self::once()) + ->method('executeQuery') + ->willReturn($result1); + + $result2 = $this + ->getMockBuilder(Result::class) + ->disableOriginalConstructor() + ->onlyMethods(['fetchFirstColumn']) + ->getMock(); + $result2 + ->expects(self::once()) + ->method('fetchFirstColumn') + ->willReturn([1, 2, 3, 4, 5]); $builder2 = $this ->getMockBuilder(QueryBuilder::class) - ->disableOriginalConstructor() - ->setMethods(['execute', 'expr']) + ->setConstructorArgs([$connection]) + ->onlyMethods(['executeQuery']) ->getMock(); - $builder2->expects($this->once())->method('expr')->willReturn(new ExpressionBuilder($connection)); - $builder2->expects($this->once())->method('execute')->willReturn($statement); + + $builder2 + ->expects(self::once()) + ->method('executeQuery') + ->willReturn($result2); $connection - ->expects($this->exactly(2)) + ->expects(self::exactly(2)) ->method('createQueryBuilder') ->willReturnOnConsecutiveCalls($builder1, $builder2); @@ -273,13 +417,30 @@ public function testSearchFor() $this->mockConfig() ); - $this->assertSame(['1', '2', '3', '4', '5'], $file->searchFor('*test?value')); + self::assertSame(['1', '2', '3', '4', '5'], $file->searchFor('*e68feb56-339b-1eb2-a675-7a5107362e40*')); + + self::assertSame( + ['value' => StringUtil::uuidToBin('e68feb56-339b-1eb2-a675-7a5107362e40')], + $builder1->getParameters() + ); + self::assertSame( + 'SELECT f.uuid, f.pid FROM tl_files f WHERE f.uuid = :value', + $builder1->getSQL() + ); - /** @var QueryBuilder $builder2 */ - $this->assertSame( - 'SELECT id FROM mm_test WHERE file_attribute IN (SELECT uuid FROM tl_files WHERE path LIKE :value)', + self::assertSame( + 'SELECT t.id FROM mm_test t WHERE ' . + '(t.file_attribute LIKE :value_0)' . + ' OR (t.file_attribute LIKE :value_1)', $builder2->getSQL() ); - $this->assertSame(['value' => '%test_value'], $builder2->getParameters()); + + self::assertSame( + [ + 'value_0' => '%' . StringUtil::uuidToBin('b4a3201a-bef2-153c-85ae-66930f01feda') . '%', + 'value_1' => '%' . StringUtil::uuidToBin('e68feb56-339b-1eb2-a675-7a5107362e40') . '%', + ], + $builder2->getParameters() + ); } } diff --git a/tests/ContaoManager/PluginTest.php b/tests/ContaoManager/PluginTest.php index a610e9c..98c1532 100644 --- a/tests/ContaoManager/PluginTest.php +++ b/tests/ContaoManager/PluginTest.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2021 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/attribute_file * @author Christian Schiffler - * @copyright 2012-2019 The MetaModels team. + * @author Sven Baumann + * @copyright 2012-2021 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,11 +24,14 @@ use Contao\ManagerPlugin\Bundle\Config\BundleConfig; use Contao\ManagerPlugin\Bundle\Parser\ParserInterface; use MetaModels\AttributeFileBundle\ContaoManager\Plugin; +use MetaModels\ContaoFrontendEditingBundle\MetaModelsContaoFrontendEditingBundle; use MetaModels\CoreBundle\MetaModelsCoreBundle; use PHPUnit\Framework\TestCase; /** * Unit tests the contao manager plugin. + * + * @covers \MetaModels\AttributeFileBundle\ContaoManager\Plugin */ class PluginTest extends TestCase { @@ -40,8 +44,8 @@ public function testInstantiation() { $plugin = new Plugin(); - $this->assertInstanceOf(Plugin::class, $plugin); - $this->assertInstanceOf(BundlePluginInterface::class, $plugin); + self::assertInstanceOf(Plugin::class, $plugin); + self::assertInstanceOf(BundlePluginInterface::class, $plugin); } /** @@ -55,13 +59,16 @@ public function testBundleConfig() $plugin = new Plugin(); $bundles = $plugin->getBundles($parser); - $this->assertContainsOnlyInstancesOf(BundleConfig::class, $bundles); - $this->assertCount(1, $bundles); + self::assertContainsOnlyInstancesOf(BundleConfig::class, $bundles); + self::assertCount(1, $bundles); /** @var BundleConfig $bundleConfig */ $bundleConfig = $bundles[0]; - $this->assertEquals($bundleConfig->getLoadAfter(), [MetaModelsCoreBundle::class]); - $this->assertEquals($bundleConfig->getReplace(), ['metamodelsattribute_file']); + self::assertEquals( + $bundleConfig->getLoadAfter(), + [MetaModelsContaoFrontendEditingBundle::class, MetaModelsCoreBundle::class] + ); + self::assertEquals($bundleConfig->getReplace(), ['metamodelsattribute_file']); } } diff --git a/tests/DependencyInjection/MetaModelsAttributeFileExtensionTest.php b/tests/DependencyInjection/MetaModelsAttributeFileExtensionTest.php index 4c7be8f..02b795d 100644 --- a/tests/DependencyInjection/MetaModelsAttributeFileExtensionTest.php +++ b/tests/DependencyInjection/MetaModelsAttributeFileExtensionTest.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,15 +12,22 @@ * * @package MetaModels/attribute_file * @author Christian Schiffler - * @copyright 2012-2019 The MetaModels team. + * @author Sven Baumann + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\AttributeFileBundle\Test\DependencyInjection; -use MetaModels\AttributeFileBundle\Attribute\AttributeTypeFactory; use MetaModels\AttributeFileBundle\DependencyInjection\MetaModelsAttributeFileExtension; +use MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\DcaSetting\FileWidgetModeOptions; +use MetaModels\ContaoFrontendEditingBundle\MetaModelsContaoFrontendEditingBundle; +use MetaModels\AttributeFileBundle\EventListener\BuildAttributeListener; +use MetaModels\AttributeFileBundle\EventListener\BuildDataDefinitionListener; +use MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsProvider; +use MetaModels\AttributeFileBundle\Schema\DoctrineSchemaGenerator; use PHPUnit\Framework\TestCase; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Definition; @@ -28,51 +35,55 @@ /** * This test case test the extension. + * + * @covers \MetaModels\AttributeFileBundle\DependencyInjection\MetaModelsAttributeFileExtension */ class MetaModelsAttributeFileExtensionTest extends TestCase { - /** - * Test that extension can be instantiated. - * - * @return void - */ - public function testInstantiation() + public function testInstantiation(): void { $extension = new MetaModelsAttributeFileExtension(); - $this->assertInstanceOf(MetaModelsAttributeFileExtension::class, $extension); - $this->assertInstanceOf(ExtensionInterface::class, $extension); + self::assertInstanceOf(MetaModelsAttributeFileExtension::class, $extension); + self::assertInstanceOf(ExtensionInterface::class, $extension); } - /** - * Test that the services are loaded. - * - * @return void - */ - public function testFactoryIsRegistered() + public function testFactoryIsRegistered(): void { - $container = $this->getMockBuilder(ContainerBuilder::class)->getMock(); - - $container - ->expects($this->atLeastOnce()) - ->method('setDefinition') - ->withConsecutive( - [ - 'metamodels.attribute_file.factory', - $this->callback( - function ($value) { - /** @var Definition $value */ - $this->assertInstanceOf(Definition::class, $value); - $this->assertEquals(AttributeTypeFactory::class, $value->getClass()); - $this->assertCount(1, $value->getTag('metamodels.attribute_factory')); - - return true; - } - ) - ] - ); + $container = new ContainerBuilder(); + $container->setParameter('kernel.debug', false); + $container->setParameter('metamodels.cache_dir', 'cache/dir'); + $container->setParameter('kernel.bundles', [MetaModelsContaoFrontendEditingBundle::class]); $extension = new MetaModelsAttributeFileExtension(); $extension->load([], $container); + + self::assertTrue($container->hasAlias('metamodels.attribute_file.toolbox.file')); + + self::assertTrue($container->hasDefinition('metamodels.attribute_file.event_listener.build_attribute')); + $definition = $container->getDefinition('metamodels.attribute_file.event_listener.build_attribute'); + self::assertCount(1, $definition->getTag('kernel.event_listener')); + + self::assertTrue($container->hasDefinition('metamodels.attribute_file.event_listener.image_size_options')); + + self::assertTrue($container->hasDefinition('metamodels.attribute_file.event_listener.build-data-definition')); + $definition = $container->getDefinition('metamodels.attribute_file.event_listener.build-data-definition'); + self::assertCount(1, $definition->getTag('kernel.event_listener')); + + self::assertTrue($container->hasDefinition(DoctrineSchemaGenerator::class)); + $definition = $container->getDefinition(DoctrineSchemaGenerator::class); + self::assertCount(1, $definition->getTag('metamodels.schema-generator.doctrine')); + + self::assertTrue($container->hasParameter('metamodels.managed-schema-type-names')); + self::assertSame(['file'], $container->getParameter('metamodels.managed-schema-type-names')); + self::assertTrue($container->hasParameter('metamodels.attribute_file.cache_dir')); + self::assertSame( + '%metamodels.cache_dir%/attribute_file', + $container->getParameter('metamodels.attribute_file.cache_dir') + ); + + self::assertTrue($container->hasDefinition(FileWidgetModeOptions::class)); + $definition = $container->getDefinition(FileWidgetModeOptions::class); + self::assertTrue($definition->getArgument('$frontendEditing')); } } diff --git a/tests/DeprecatedAutoloaderTest.php b/tests/DeprecatedAutoloaderTest.php index 3267fba..c2302fb 100644 --- a/tests/DeprecatedAutoloaderTest.php +++ b/tests/DeprecatedAutoloaderTest.php @@ -25,12 +25,17 @@ use MetaModels\AttributeFileBundle\Attribute\AttributeTypeFactory; use MetaModels\AttributeFileBundle\Attribute\FileOrder; use MetaModels\AttributeFileBundle\DcGeneral\AttributeFileDefinition; -use MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsListener; -use MetaModels\AttributeFileBundle\Helper\UpgradeHandler; +use MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsProvider; use PHPUnit\Framework\TestCase; /** * This class tests if the deprecated autoloader works. + * + * @covers \MetaModels\AttributeFileBundle\Attribute\File + * @covers \MetaModels\AttributeFileBundle\Attribute\FileOrder + * @covers \MetaModels\AttributeFileBundle\Attribute\AttributeTypeFactory + * @covers \MetaModels\AttributeFileBundle\DcGeneral\AttributeFileDefinition + * @covers \MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsProvider */ class DeprecatedAutoloaderTest extends TestCase { @@ -43,9 +48,8 @@ class DeprecatedAutoloaderTest extends TestCase 'MetaModels\Attribute\File\File' => File::class, 'MetaModels\Attribute\File\FileOrder' => FileOrder::class, 'MetaModels\Attribute\File\AttributeTypeFactory' => AttributeTypeFactory::class, - 'MetaModels\Attribute\File\Helper\UpgradeHandler' => UpgradeHandler::class, 'MetaModels\DcGeneral\AttributeFileDefinition' => AttributeFileDefinition::class, - 'MetaModels\Events\Attribute\File\ImageSizeOptions' => ImageSizeOptionsListener::class, + 'MetaModels\Events\Attribute\File\ImageSizeOptions' => ImageSizeOptionsProvider::class, ]; /** @@ -74,11 +78,11 @@ public function provideAliasClassMap() */ public function testDeprecatedClassesAreAliased($oldClass, $newClass) { - $this->assertTrue(\class_exists($oldClass), \sprintf('Class select "%s" is not found.', $oldClass)); + self::assertTrue(\class_exists($oldClass), \sprintf('Class select "%s" is not found.', $oldClass)); $oldClassReflection = new \ReflectionClass($oldClass); $newClassReflection = new \ReflectionClass($newClass); - $this->assertSame($newClassReflection->getFileName(), $oldClassReflection->getFileName()); + self::assertSame($newClassReflection->getFileName(), $oldClassReflection->getFileName()); } } diff --git a/tests/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptionsTest.php b/tests/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptionsTest.php index 06caf8b..5144ef2 100644 --- a/tests/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptionsTest.php +++ b/tests/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptionsTest.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2021 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package MetaModels/attribute_file * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2021 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -41,27 +41,23 @@ private function mockEnvironment() $environment = $this->getMockForAbstractClass(EnvironmentInterface::class); $environment - ->expects($this->any()) + ->expects(self::any()) ->method('getDataDefinition') - ->will( - $this->returnCallback( - function () use (&$dataDefinition) { - return $dataDefinition; - } - ) + ->willReturnCallback( + function () use (&$dataDefinition) { + return $dataDefinition; + } ); $environment - ->expects($this->any()) + ->expects(self::any()) ->method('setDataDefinition') - ->will( - $this->returnCallback( - function (ContainerInterface $container) use (&$dataDefinition, $environment) { - $dataDefinition = $container; - - return $environment; - } - ) + ->willReturnCallback( + function (ContainerInterface $container) use (&$dataDefinition, $environment) { + $dataDefinition = $container; + + return $environment; + } ); return $environment; @@ -72,7 +68,7 @@ private function mockDataDefinition($name = null) $dataDefinition = $this->getMockForAbstractClass(ContainerInterface::class); $dataDefinition - ->expects($this->any()) + ->expects(self::any()) ->method('getName') ->willReturn($name); @@ -112,8 +108,8 @@ public function testRemoveOption(array $expected, $providerName, $propertyName, $event = new GetPropertyOptionsEvent($environment, $this->mockModel()); $event->setPropertyName($propertyName); $event->setOptions($options); - $dispatcher->dispatch($event::NAME, $event); + $dispatcher->dispatch($event, $event::NAME); - $this->assertSame($expected, $event->getOptions()); + self::assertSame($expected, $event->getOptions()); } } diff --git a/tests/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptionsTest.php b/tests/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptionsTest.php index d5ec804..0d165d4 100644 --- a/tests/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptionsTest.php +++ b/tests/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptionsTest.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/attribute_file * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -41,27 +42,23 @@ private function mockEnvironment() $environment = $this->getMockForAbstractClass(EnvironmentInterface::class); $environment - ->expects($this->any()) + ->expects(self::any()) ->method('getDataDefinition') - ->will( - $this->returnCallback( - function () use (&$dataDefinition) { - return $dataDefinition; - } - ) + ->willReturnCallback( + function () use (&$dataDefinition) { + return $dataDefinition; + } ); $environment - ->expects($this->any()) + ->expects(self::any()) ->method('setDataDefinition') - ->will( - $this->returnCallback( - function (ContainerInterface $container) use (&$dataDefinition, $environment) { - $dataDefinition = $container; - - return $environment; - } - ) + ->willReturnCallback( + function (ContainerInterface $container) use (&$dataDefinition, $environment) { + $dataDefinition = $container; + + return $environment; + } ); return $environment; @@ -72,7 +69,7 @@ private function mockDataDefinition($name = null) $dataDefinition = $this->getMockForAbstractClass(ContainerInterface::class); $dataDefinition - ->expects($this->any()) + ->expects(self::any()) ->method('getName') ->willReturn($name); @@ -89,9 +86,19 @@ public function dataProviderTestRemoveOption() return [ [['foo' => 'bar [file]', 'filesort' => 'foo'], 'foo', 'foo', ['foo' => 'bar [file]', 'filesort' => 'foo']], - [['foo' => 'bar [file]', 'filesort' => 'foo'], 'foo', 'attr_id', ['foo' => 'bar [file]', 'filesort' => 'foo']], + [ + ['foo' => 'bar [file]', 'filesort' => 'foo'], + 'foo', + 'attr_id', + ['foo' => 'bar [file]', 'filesort' => 'foo'] + ], [['foo' => 'bar [file]'], 'tl_metamodel_filtersetting', 'attr_id', ['foo' => 'bar [file]']], - [['foo' => 'bar [file]'], 'tl_metamodel_filtersetting', 'attr_id', ['foo' => 'bar [file]', 'foo__sort' => 'foo']] + [ + ['foo' => 'bar [file]'], + 'tl_metamodel_filtersetting', + 'attr_id', + ['foo' => 'bar [file]', 'foo__sort' => 'foo'] + ] ]; } @@ -112,8 +119,8 @@ public function testRemoveOption(array $expected, $providerName, $propertyName, $event = new GetPropertyOptionsEvent($environment, $this->mockModel()); $event->setPropertyName($propertyName); $event->setOptions($options); - $dispatcher->dispatch($event::NAME, $event); + $dispatcher->dispatch($event, $event::NAME); - $this->assertSame($expected, $event->getOptions()); + self::assertSame($expected, $event->getOptions()); } } diff --git a/tests/EventListener/Factory/AddAttributeInformationTest.php b/tests/EventListener/Factory/AddAttributeInformationTest.php index 4db9099..ad0f233 100644 --- a/tests/EventListener/Factory/AddAttributeInformationTest.php +++ b/tests/EventListener/Factory/AddAttributeInformationTest.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/core * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,6 +33,7 @@ */ class AddAttributeInformationTest extends TestCase { + /** @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function dataProviderAddInformation() { return [ @@ -185,8 +187,8 @@ public function testAddInformation($expected, $information) $metaModel = $this->getMockForAbstractClass(IMetaModel::class); $event = new CollectMetaModelAttributeInformationEvent($metaModel); $event->setAttributeInformation($information); - $dispatcher->dispatch($event::NAME, $event); + $dispatcher->dispatch($event, $event::NAME); - $this->assertSame($expected, $event->getAttributeInformation()); + self::assertSame($expected, $event->getAttributeInformation()); } } diff --git a/tests/Schema/DoctrineSchemaGeneratorTest.php b/tests/Schema/DoctrineSchemaGeneratorTest.php new file mode 100644 index 0000000..b0786bd --- /dev/null +++ b/tests/Schema/DoctrineSchemaGeneratorTest.php @@ -0,0 +1,92 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\AttributeFileBundle\Test\Schema; + +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Types\Types; +use MetaModels\AttributeFileBundle\Schema\DoctrineSchemaGenerator; +use MetaModels\Information\AttributeInformation; +use PHPUnit\Framework\TestCase; + +/** + * This tests the schema generator. + * + * @covers \MetaModels\AttributeFileBundle\Schema\DoctrineSchemaGenerator + */ +class DoctrineSchemaGeneratorTest extends TestCase +{ + /** + * Test the generate method. + * + * @return void + */ + public function testGenerate(): void + { + $instance = new DoctrineSchemaGenerator(); + $reflection = new \ReflectionMethod(DoctrineSchemaGenerator::class, 'generateAttribute'); + $reflection->setAccessible(true); + + $tableSchema = new Table('mm_test'); + $attribute = new AttributeInformation('test', 'file', ['file_multiple' => false]); + + $reflection->invoke($instance, $tableSchema, $attribute); + + $this->assertTrue($tableSchema->hasColumn('test')); + $column = $tableSchema->getColumn('test'); + $this->assertSame('test', $column->getName()); + $this->assertSame(Type::getType(Types::BLOB), $column->getType()); + $this->assertSame(false, $column->getNotnull()); + + $this->assertFalse($tableSchema->hasColumn('test__sort')); + } + + /** + * Test the generate method. + * + * @return void + */ + public function testGenerateForMultiple(): void + { + $instance = new DoctrineSchemaGenerator(); + $reflection = new \ReflectionMethod(DoctrineSchemaGenerator::class, 'generateAttribute'); + $reflection->setAccessible(true); + + $tableSchema = new Table('mm_test'); + $attribute = new AttributeInformation('test', 'file', ['file_multiple' => true]); + + $reflection->invoke($instance, $tableSchema, $attribute); + + $this->assertTrue($tableSchema->hasColumn('test')); + $column = $tableSchema->getColumn('test'); + $this->assertSame('test', $column->getName()); + $this->assertSame(Type::getType(Types::BLOB), $column->getType()); + $this->assertSame(false, $column->getNotnull()); + + $this->assertTrue($tableSchema->hasColumn('test__sort')); + $column = $tableSchema->getColumn('test__sort'); + $this->assertSame('test__sort', $column->getName()); + $this->assertSame(Type::getType(Types::BLOB), $column->getType()); + $this->assertSame(false, $column->getNotnull()); + } +}