diff --git a/.check-author.yml b/.check-author.yml deleted file mode 100644 index c6d7b3e..0000000 --- a/.check-author.yml +++ /dev/null @@ -1,12 +0,0 @@ -ignore: - - 'Mini Model ' - -exclude: - - /^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 ff8e188..eccdc19 100644 --- a/.gitignore +++ b/.gitignore @@ -2,17 +2,20 @@ .DS_Store Thumbs.db -# IDEs +# IDEs .buildpath .project .settings/ .build/ .external*/ .idea/ +.phpcq/ nbproject/ # composer related vendor/ # build -build/ \ No newline at end of file +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 f09e23b..0000000 --- a/.travis.yml +++ /dev/null @@ -1,47 +0,0 @@ -dist: xenial - -addons: - apt: - packages: - - ant-optional - -language: php - -php: - - "7.3" - - "7.2" - - "7.1" - - "7.0" - - "5.6" - -env: - - CONTAO_VERSION=~3.5.5 - -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 $CONTAO_VERSION --no-update - - > - if [ "x${TRAVIS_TAG}" != "x" ]; then - export COMPOSER_ROOT_VERSION=${TRAVIS_TAG} - else - export COMPOSER_ROOT_VERSION=$([[ ${TRAVIS_BRANCH} =~ hotfix/([0-9.]*(-(alpha|beta|rc)[0-9]+)?) ]] \ - && echo ${BASH_REMATCH[1]} \ - || echo dev-${TRAVIS_BRANCH}) - fi - - echo "Using root version ${COMPOSER_ROOT_VERSION}" - - travis_retry composer update --prefer-dist --no-interaction - -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 169b17a..554eebe 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,9 @@ -[![Stable Build Status](http://img.shields.io/travis/MetaModels/attribute_file/master.svg?label=stable)](https://travis-ci.org/MetaModels/attribute_file/branches) -[![Development Build Status](http://img.shields.io/travis/MetaModels/attribute_file/develop.svg?label=develop)](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) -[![Dependency Status](https://www.versioneye.com/php/metamodels:attribute_file/badge.svg)](https://www.versioneye.com/php/metamodels:attribute_file) File ==== -The file attribute +The file attribute. diff --git a/build.default.properties b/build.default.properties deleted file mode 100644 index e3fa8ea..0000000 --- a/build.default.properties +++ /dev/null @@ -1,9 +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 diff --git a/build.xml b/build.xml deleted file mode 100644 index 05f3d5d..0000000 --- a/build.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - diff --git a/composer.json b/composer.json index 65c46cc..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-module", - "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,55 +23,60 @@ "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": "^5.6 || ^7.0", - "contao-community-alliance/composer-plugin": "^2.4", - "contao-community-alliance/dc-general": "^2.0.0", - "contao/core": "^3.5.5", - "metamodels/core": "^2.0" + "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": { - "metamodels/base-unit-tests": "^1.0.4", - "phpcq/all-tasks": "^1.2" + "contao/manager-plugin": "^2.1", + "inspiredminds/contao-file-usage": "^3.0.1", + "menatwork/contao-multicolumnwizard-bundle": "^3.4", + "metamodels/contao-frontend-editing": "^2.3", + "phpcq/runner-bootstrap": "^1.0@dev" }, "autoload": { - "psr-0": { - "MetaModels\\": "src" - } + "psr-4": { + "MetaModels\\AttributeFileBundle\\": "src" + }, + "files": [ + "src/deprecated-autoload.php" + ] }, "autoload-dev": { - "psr-0": { - "MetaModels\\Test\\": "tests" - } - }, - "extra": { - "contao": { - "sources": { - "contao": "system/modules/metamodelsattribute_file" - }, - "runonce": [ - "runonce/runonce.php" - ], - "transifex": { - "project": "metamodels", - "prefix": "attribute_file-", - "languages_cto": "contao/languages", - "languages_tx": ".tx" - } - }, - "branch-alias": { - "dev-master": "2.0.x-dev", - "dev-develop": "2.1.x-dev" + "psr-4": { + "MetaModels\\AttributeFileBundle\\Test\\": "tests" } }, "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/contao/config/autoload.ini b/contao/config/autoload.ini deleted file mode 100644 index c8f07a2..0000000 --- a/contao/config/autoload.ini +++ /dev/null @@ -1,7 +0,0 @@ - -;; -; Configure what you want the autoload creator to register -;; -register_namespaces = false -register_classes = false -register_templates = true diff --git a/contao/config/event_listeners.php b/contao/config/event_listeners.php deleted file mode 100644 index 10eaaba..0000000 --- a/contao/config/event_listeners.php +++ /dev/null @@ -1,56 +0,0 @@ - - * @author David Molineus - * @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 - */ - -use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; -use MetaModels\Attribute\Events\CollectMetaModelAttributeInformationEvent; -use MetaModels\Attribute\Events\CreateAttributeFactoryEvent; -use MetaModels\Attribute\File\AttributeOrderTypeFactory; -use MetaModels\Attribute\File\AttributeTypeFactory; -use MetaModels\Attribute\File\Subscriber; -use MetaModels\Events\Attribute\File\AddAttributeInformation; -use MetaModels\Events\Attribute\File\ImageSizeOptions; -use MetaModels\Events\DcGeneral\Table\Attribute\File\RemoveTypeOptions; -use MetaModels\Events\DcGeneral\Table\FilterSetting\File\RemoveAttIdOptions; -use MetaModels\Events\MetaModelsBootEvent; -use MetaModels\MetaModelsEvents; - -return [ - MetaModelsEvents::ATTRIBUTE_FACTORY_CREATE => [ - function (CreateAttributeFactoryEvent $event) { - $factory = $event->getFactory(); - $factory->addTypeFactory(new AttributeTypeFactory()); - $factory->addTypeFactory(new AttributeOrderTypeFactory()); - } - ], - CollectMetaModelAttributeInformationEvent::NAME => [ - [[new AddAttributeInformation(), 'addInformation'], -1] - ], - MetaModelsEvents::SUBSYSTEM_BOOT_BACKEND => [ - function (MetaModelsBootEvent $event) { - new Subscriber($event->getServiceContainer()); - } - ], - GetPropertyOptionsEvent::NAME => [ - [new ImageSizeOptions(), 'getPropertyOptions'], - [[new RemoveTypeOptions(), 'removeOption'], -1], - [[new RemoveAttIdOptions(), 'removeOption'], -1] - ] -]; diff --git a/contao/dca/tl_metamodel_attribute.php b/contao/dca/tl_metamodel_attribute.php deleted file mode 100644 index 819a834..0000000 --- a/contao/dca/tl_metamodel_attribute.php +++ /dev/null @@ -1,69 +0,0 @@ - - * @author Stefan Heimes - * @author Andreas Isaak - * @author David Molineus - * @author Sven Baumann - * @author Ingolf Steinhardt - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_DCA']['tl_metamodel_attribute']['metapalettes']['file extends _simpleattribute_'] = [ - '+advanced' => ['file_customFiletree', 'file_multiple'], -]; - -$GLOBALS['TL_DCA']['tl_metamodel_attribute']['metasubpalettes']['file_customFiletree'] = [ - 'file_uploadFolder', - 'file_validFileTypes', - 'file_filesOnly' -]; - -$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'] -]; - -$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'] -]; - -$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'] -]; - -$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'] -]; - -$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'] -]; diff --git a/contao/dca/tl_metamodel_rendersetting.php b/contao/dca/tl_metamodel_rendersetting.php deleted file mode 100644 index 10e5a41..0000000 --- a/contao/dca/tl_metamodel_rendersetting.php +++ /dev/null @@ -1,79 +0,0 @@ - - * @author Andreas Isaak - * @author Sven Baumann - * @author Ingolf Steinhardt - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -/** - * Table tl_metamodel_attribute - */ - -$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_showImage'] = [ - 'file_imageSize', -]; - -$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' => [ - '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'] -]; - -$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' => [ - 'submitOnChange' => true, - 'tl_class' => 'clr' - ] -]; - -$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', - 'includeBlankOption' => true, - 'nospace' => true, - 'helpwizard' => true, - 'tl_class' => 'w50' - ] -]; diff --git a/contao/html/.htaccess b/contao/html/.htaccess deleted file mode 100644 index 1c68f30..0000000 --- a/contao/html/.htaccess +++ /dev/null @@ -1,7 +0,0 @@ - - Require all granted - - - Order Deny,Allow - Allow from all - diff --git a/contao/languages/de/tl_metamodel_attribute.php b/contao/languages/de/tl_metamodel_attribute.php deleted file mode 100644 index a86eb0e..0000000 --- a/contao/languages/de/tl_metamodel_attribute.php +++ /dev/null @@ -1,26 +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/contao/languages/en/tl_metamodel_dcasetting.php b/contao/languages/en/tl_metamodel_dcasetting.php deleted file mode 100644 index 3a71030..0000000 --- a/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/contao/languages/en/tl_metamodel_rendersetting.php b/contao/languages/en/tl_metamodel_rendersetting.php deleted file mode 100644 index 15bd231..0000000 --- a/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/contao/languages/et/tl_metamodel_attribute.php b/contao/languages/et/tl_metamodel_attribute.php deleted file mode 100644 index d154fd1..0000000 --- a/contao/languages/et/tl_metamodel_attribute.php +++ /dev/null @@ -1,19 +0,0 @@ -src)): ?> - - diff --git a/contao/templates/mm_attr_file.text b/contao/templates/mm_attr_file.text deleted file mode 100644 index 03c1b3d..0000000 --- a/contao/templates/mm_attr_file.text +++ /dev/null @@ -1 +0,0 @@ -files) ?> diff --git a/contao/templates/mm_attr_file.xhtml b/contao/templates/mm_attr_file.xhtml deleted file mode 100644 index 958df13..0000000 --- a/contao/templates/mm_attr_file.xhtml +++ /dev/null @@ -1,30 +0,0 @@ -src)): ?> - - diff --git a/contao/templates/mm_attr_file_alternative.html5 b/contao/templates/mm_attr_file_alternative.html5 deleted file mode 100644 index af3c7b4..0000000 --- a/contao/templates/mm_attr_file_alternative.html5 +++ /dev/null @@ -1,34 +0,0 @@ -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')): ?> - -
- -
- -

- - diff --git a/contao/templates/mm_attr_file_alternative.text b/contao/templates/mm_attr_file_alternative.text deleted file mode 100644 index e9bad88..0000000 --- a/contao/templates/mm_attr_file_alternative.text +++ /dev/null @@ -1,3 +0,0 @@ -raw['path'] as $file): ?> - - \ No newline at end of file diff --git a/contao/templates/mm_attr_file_alternative.xhtml b/contao/templates/mm_attr_file_alternative.xhtml deleted file mode 100644 index ba0c4b1..0000000 --- a/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/ctb.json b/ctb.json index 02980b1..eb187ce 100644 --- a/ctb.json +++ b/ctb.json @@ -2,7 +2,7 @@ "transifex": { "project": "metamodels", "prefix": "attribute_file-", - "languages_cto": "contao/languages", + "languages_cto": "src/Resources/contao/languages", "languages_tx": ".tx", "php-file-header": [ "This file is part of MetaModels/attribute_file.", 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/runonce/runonce.php b/runonce/runonce.php deleted file mode 100644 index c1efe41..0000000 --- a/runonce/runonce.php +++ /dev/null @@ -1,23 +0,0 @@ - - * @copyright 2012-2016 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0 - * @filesource - */ - -// Let our handler handle the necessary steps. -$handler = new MetaModels\Attribute\File\Helper\UpgradeHandler(\Database::getInstance()); -$handler->perform(); diff --git a/src/Attribute/AttributeOrderTypeFactory.php b/src/Attribute/AttributeOrderTypeFactory.php new file mode 100644 index 0000000..3cb7599 --- /dev/null +++ b/src/Attribute/AttributeOrderTypeFactory.php @@ -0,0 +1,126 @@ + + * @author Benedict Zinke + * @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\Attribute; + +use Doctrine\DBAL\Connection; +use MetaModels\Attribute\IAttributeTypeFactory; +use MetaModels\Helper\TableManipulator; + +/** + * Attribute type factory for file order attributes. + */ +class AttributeOrderTypeFactory implements IAttributeTypeFactory +{ + /** + * Database connection. + * + * @var Connection + */ + protected $connection; + + /** + * Table manipulator. + * + * @var TableManipulator + */ + protected $tableManipulator; + + /** + * Cache table columns. + * + * @var array + */ + private $tableColumns = []; + + /** + * {@inheritDoc} + * + * @param Connection $connection The database connection. + * @param TableManipulator $tableManipulator The table manipulator. + * @param string $rootPath The root path. + */ + public function __construct( + Connection $connection, + TableManipulator $tableManipulator + ) { + $this->connection = $connection; + $this->tableManipulator = $tableManipulator; + } + + /** + * {@inheritDoc} + */ + public function getTypeName() + { + return 'filesort'; + } + + /** + * {@inheritDoc} + */ + public function getTypeIcon() + { + return ''; + } + + /** + * {@inheritDoc} + */ + public function createInstance($information, $metaModel) + { + $columnName = (string) ($information['colname'] ?? ''); + $tableName = $metaModel->getTableName(); + + if (!isset($this->tableColumns[$tableName])) { + $this->tableColumns[$tableName] = $this->connection->createSchemaManager()->listTableColumns($tableName); + } + + if (('' === $columnName) || !\array_key_exists($columnName, $this->tableColumns[$tableName])) { + return null; + } + + return new FileOrder($metaModel, $information, $this->connection); + } + + /** + * {@inheritDoc} + */ + public function isTranslatedType() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function isSimpleType() + { + return true; + } + + /** + * {@inheritDoc} + */ + public function isComplexType() + { + return false; + } +} diff --git a/src/Attribute/AttributeTypeFactory.php b/src/Attribute/AttributeTypeFactory.php new file mode 100644 index 0000000..4f35ee0 --- /dev/null +++ b/src/Attribute/AttributeTypeFactory.php @@ -0,0 +1,175 @@ + + * @author Sven Baumann + * @author Stefan Heimes + * @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\Attribute; + +use Contao\Config; +use Contao\CoreBundle\Framework\Adapter; +use Contao\FilesModel; +use Contao\StringUtil; +use Doctrine\DBAL\Connection; +use MetaModels\Attribute\IAttributeTypeFactory; +use MetaModels\Helper\TableManipulator; +use MetaModels\Helper\ToolboxFile; +use Validator; + +/** + * Attribute type factory for file attributes. + */ +class AttributeTypeFactory implements IAttributeTypeFactory +{ + /** + * Database connection. + * + * @var Connection + */ + protected $connection; + + /** + * Table manipulator. + * + * @var TableManipulator + */ + protected $tableManipulator; + + /** + * The toolbox for file. + * + * @var ToolboxFile + */ + private ToolboxFile $toolboxFile; + + /** + * The string util. + * + * @var Adapter + */ + private Adapter $stringUtil; + + /** + * The validator. + * + * @var Adapter + */ + private Adapter $validator; + + /** + * The repository for files. + * + * @var Adapter + */ + private Adapter $fileRepository; + + /** + * The contao configurations. + * + * @var Adapter + */ + 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 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, + TableManipulator $tableManipulator, + ToolboxFile $toolboxFile, + Adapter $stringUtil, + Adapter $validator, + Adapter $fileRepository, + Adapter $config + ) { + $this->connection = $connection; + $this->tableManipulator = $tableManipulator; + $this->toolboxFile = $toolboxFile; + $this->stringUtil = $stringUtil; + $this->validator = $validator; + $this->fileRepository = $fileRepository; + $this->config = $config; + } + + /** + * {@inheritDoc} + */ + public function getTypeName() + { + return 'file'; + } + + /** + * {@inheritDoc} + */ + public function getTypeIcon() + { + return 'bundles/metamodelsattributefile/file.png'; + } + + /** + * {@inheritDoc} + */ + public function createInstance($information, $metaModel) + { + return new File( + $metaModel, + $information, + $this->connection, + $this->tableManipulator, + $this->toolboxFile, + $this->stringUtil, + $this->validator, + $this->fileRepository, + $this->config + ); + } + + /** + * {@inheritDoc} + */ + public function isTranslatedType() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function isSimpleType() + { + return false; + } + + /** + * {@inheritDoc} + */ + public function isComplexType() + { + return true; + } +} diff --git a/src/Attribute/File.php b/src/Attribute/File.php new file mode 100644 index 0000000..a3bdb6f --- /dev/null +++ b/src/Attribute/File.php @@ -0,0 +1,665 @@ + + * @author Andreas Isaak + * @author Christopher Boelter + * @author David Greminger + * @author David Maack + * @author MrTool + * @author Oliver Hoff + * @author Stefan Heimes + * @author Marc Reimann + * @author David Molineus + * @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\Attribute; + +use Contao\Config; +use Contao\CoreBundle\Framework\Adapter; +use Contao\FilesModel; +use Contao\StringUtil; +use Contao\System; +use Contao\Validator; +use Doctrine\DBAL\ArrayParameterType; +use Doctrine\DBAL\Connection; +use MetaModels\Attribute\BaseComplex; +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 + */ + private Connection $connection; + + /** + * Table manipulator instance. + * + * @var TableManipulator + */ + private TableManipulator $tableManipulator; + + /** + * The toolbox for file. + * + * @var ToolboxFile + */ + private ToolboxFile $toolboxFile; + + /** + * The string util. + * + * @var Adapter + */ + private Adapter $stringUtil; + + /** + * The validator. + * + * @var Adapter + */ + private Adapter $validator; + + /** + * The repository for files. + * + * @var Adapter + */ + private Adapter $fileRepository; + + /** + * The contao configurations. + * + * @var Adapter + */ + 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|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, + $information = [], + Connection $connection = null, + TableManipulator $tableManipulator = null, + ToolboxFile $toolboxFile = null, + Adapter $stringUtil = null, + Adapter $validator = null, + Adapter $fileRepository = null, + Adapter $config = null + ) { + parent::__construct($metaModel, $information); + + 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); + } + + if (null === $toolboxFile) { + $toolboxFile = $this->fetchServiceForFallback('toolboxFile', 'MetaModels\Helper\ToolboxFile'); + assert($toolboxFile instanceof ToolboxFile); + } + + if (null === $stringUtil) { + // @codingStandardsIgnoreStart + @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); + assert($stringUtil instanceof Adapter); + } + + if (null === $validator) { + // @codingStandardsIgnoreStart + @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); + assert($validator instanceof Adapter); + } + + if (null === $fileRepository) { + // @codingStandardsIgnoreStart + @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); + assert($fileRepository instanceof Adapter); + } + + if (null === $config) { + // @codingStandardsIgnoreStart + @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); + assert($config instanceof Adapter); + } + + $this->connection = $connection; + $this->tableManipulator = $tableManipulator; + $this->toolboxFile = $toolboxFile; + $this->stringUtil = $stringUtil; + $this->validator = $validator; + $this->fileRepository = $fileRepository; + $this->config = $config; + } + + /** + * {@inheritdoc} + */ + 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->createSchemaManager()->listTableColumns($metaModel); + if (($colName = $this->getColName()) && array_key_exists($colName, $tableColumns)) { + $this->tableManipulator->dropColumn($metaModel, $colName); + } + + if (array_key_exists($colName . '__sort', $tableColumns)) { + $this->tableManipulator->dropColumn($metaModel, $colName . '__sort'); + } + } + + /** + * {@inheritdoc} + */ + 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->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): ?array + { + $subSelect = $this->connection->createQueryBuilder(); + $subSelect + ->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('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 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()); + } + + /** + * {@inheritdoc} + */ + public function unsetDataFor($arrIds) + { + $builder = $this->connection->createQueryBuilder(); + $builder + ->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('t.' . $this->getColName() . '__sort', ':null'); + } + + $builder->executeQuery(); + } + + /** + * {@inheritdoc} + */ + public function getDataFor($arrIds) + { + $builder = $this->connection->createQueryBuilder(); + + $builder + ->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')) { + $builder->addSelect($this->getColName() . '__sort AS file_sort'); + } + + $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); + + foreach ($this->toolboxFile->convertValuesToMetaModels($sorted) as $sortedKey => $sortedValue) { + $row[$sortedKey . '_sorted'] = $sortedValue; + } + + if (isset($row['sort'])) { + // @codingStandardsIgnoreStart + @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 + ); + // @codingStandardsIgnoreEnd + } + } + + $data[$result['id']] = $row; + } + + return $data; + } + + /** + * This method is called to store the data for certain items to the database. + * + * @param mixed $arrValues The values to be stored into database. Mapping is item id=>value. + * + * @return void + */ + public function setDataFor($arrValues) + { + foreach ($arrValues as $id => $value) { + if (null === $value) { + // The sort key be can remove in later version. + $value = ['bin' => [], 'value' => [], 'path' => [], 'sort' => null]; + } + + $files = ToolboxFile::convertValuesToDatabase($value); + + // Check single file or multiple file. + if ($this->get('file_multiple')) { + $files = serialize($files); + } else { + $files = $files[0] ?? null; + } + + $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(); + } + } + + /** + * {@inheritDoc} + */ + public function getFilterOptions($idList, $usedOnly, &$arrCount = null) + { + return []; + } + + /** + * {@inheritdoc} + */ + public function getAttributeSettingNames() + { + return array_merge( + parent::getAttributeSettingNames(), + [ + 'file_multiple', + 'file_customFiletree', + 'file_uploadFolder', + 'file_validFileTypes', + 'file_filesOnly', + 'file_widgetMode', + 'filterable', + 'searchable', + 'mandatory' + ] + ); + } + + /** + * Take the raw data from the DB column and unserialize it. + * + * @param mixed $value The array of data from the database. + * + * @return array + */ + public function unserializeData($value) + { + return ToolboxFile::convertValuesToMetaModels($this->stringUtil->deserialize($value, true)); + } + + /** + * Take the data from the system and serialize it for the database. + * + * @param mixed $mixValues The data to serialize. + * + * @return string An serialized array with binary data or a binary data. + */ + public function serializeData($mixValues) + { + $data = ToolboxFile::convertValuesToDatabase($mixValues ?: ['bin' => [], 'value' => [], 'path' => []]); + + // Check single file or multiple file. + if ($this->get('file_multiple')) { + return serialize($data); + } + + return $data[0] ?? ''; + } + + /** + * Manipulate the field definition for custom file trees. + * + * @param array $arrFieldDef The field definition to manipulate. + * + * @return void + */ + private function handleCustomFileTree(&$arrFieldDef) + { + if ($this->get('file_uploadFolder')) { + // 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') ?? ''); + } + + // Check if we have a file. + if (null !== $file) { + $arrFieldDef['eval']['path'] = $file->path; + } else { + // Fallback. + $arrFieldDef['eval']['path'] = $this->get('file_uploadFolder'); + } + } + + if ($this->get('file_validFileTypes')) { + $arrFieldDef['eval']['extensions'] = $this->get('file_validFileTypes'); + } + + 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. + } + } + + /** + * {@inheritdoc} + */ + public function getFieldDefinition($arrOverrides = []) + { + $fieldDefinition = parent::getFieldDefinition($arrOverrides); + + $fieldDefinition['inputType'] = 'fileTree'; + $fieldDefinition['eval']['files'] = true; + $fieldDefinition['eval']['extensions'] = $this->config->get('allowedDownload'); + $fieldDefinition['eval']['multiple'] = (bool) $this->get('file_multiple'); + + $widgetMode = $this->getOverrideValue('file_widgetMode', $arrOverrides); + + if (('normal' !== $widgetMode) && ((bool) $this->get('file_multiple'))) { + $fieldDefinition['eval']['orderField'] = $this->getColName() . '__sort'; + } + + $fieldDefinition['eval']['isDownloads'] = ('downloads' === $widgetMode); + $fieldDefinition['eval']['isGallery'] = ('gallery' === $widgetMode); + + if ($this->get('file_multiple')) { + $fieldDefinition['eval']['fieldType'] = 'checkbox'; + } else { + $fieldDefinition['eval']['fieldType'] = 'radio'; + } + + if ($this->get('file_customFiletree')) { + $this->handleCustomFileTree($fieldDefinition); + } + + return $fieldDefinition; + } + + /** + * {@inheritdoc} + */ + public function valueToWidget($varValue) + { + return $this->get('file_multiple') ? ($varValue['bin'] ?? null) : ($varValue['bin'][0] ?? null); + } + + /** + * {@inheritdoc} + */ + public function widgetToValue($varValue, $itemId) + { + return ToolboxFile::convertUuidsOrPathsToMetaModels((array) $varValue); + } + + /** + * {@inheritDoc} + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + protected function prepareTemplate(Template $objTemplate, $arrRowData, $objSettings) + { + 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' => []]; + } + + $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( + '%s.%s.%s', + $this->getMetaModel()->getTableName(), + (string) ($objSettings->get('id') ?? ''), + (string) ($arrRowData['id'] ?? '0') + ) + ) + ->setShowImages($showImage); + + if ($this->get('file_validFileTypes')) { + $toolbox->setAcceptedExtensions($this->get('file_validFileTypes')); + } + + if (is_array($imageSize = $objSettings->get('file_imageSize'))) { + $toolbox->setResizeImages($imageSize); + } + + 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($objSettings->get('file_sortBy') ?? 'name_asc', ($value['bin_sorted'] ?? [])); + + $objTemplate->files = $data['files']; + $objTemplate->src = $data['source']; + } + + private function fetchServiceForFallback(string $parameter, string $serviceName): null|object + { + // @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 System::getContainer()->get($serviceName); + } +} diff --git a/src/MetaModels/Attribute/File/FileOrder.php b/src/Attribute/FileOrder.php similarity index 85% rename from src/MetaModels/Attribute/File/FileOrder.php rename to src/Attribute/FileOrder.php index c21fc94..9f0faf7 100644 --- a/src/MetaModels/Attribute/File/FileOrder.php +++ b/src/Attribute/FileOrder.php @@ -3,25 +3,27 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2017 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. * * This project is provided in good faith and hope to be usable by anyone. * - * @package MetaModels - * @subpackage AttributeFile + * @package MetaModels/attribute_file * @author Christian Schiffler - * @author David Molineus * @author Sven Baumann - * @copyright 2012-2017 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0 + * @author Stefan Heimes + * @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 */ -namespace MetaModels\Attribute\File; +namespace MetaModels\AttributeFileBundle\Attribute; +use Contao\StringUtil; +use Doctrine\DBAL\Connection; use MetaModels\Attribute\ISimple; use MetaModels\Attribute\IInternal; use MetaModels\IMetaModel; @@ -29,8 +31,6 @@ /** * FileOrder is a helper attribute for the file attribute. * - * @package MetaModels\Attribute\File - * * @SuppressWarnings(PHPMD.TooManyPublicMethods) */ class FileOrder implements ISimple, IInternal @@ -40,25 +40,34 @@ 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 $connection; /** * Create a new instance. * * @param IMetaModel $metaModel The MetaModel. * @param array $information The attribute information. + * @param Connection $connection The connection. */ - public function __construct(IMetaModel $metaModel, array $information) + public function __construct(IMetaModel $metaModel, array $information, Connection $connection) { - $this->metaModel = $metaModel; - $this->colName = $information['colname']; + $this->metaModel = $metaModel; + $this->colName = $information['colname']; + $this->connection = $connection; } /** @@ -66,7 +75,7 @@ public function __construct(IMetaModel $metaModel, array $information) */ public function getName() { - return null; + return ''; } /** @@ -176,9 +185,7 @@ public function valueToWidget($varValue) */ public function widgetToValue($varValue, $itemId) { - $varValue = $this->unserializeData($varValue); - - return $varValue; + return $this->unserializeData($varValue); } /** @@ -186,21 +193,15 @@ public function widgetToValue($varValue, $itemId) */ public function setDataFor($arrValues) { - foreach ($arrValues as $id => $varData) { - if ($varData === null) { - $varData = array(); - } - - $this->getMetaModel()->getServiceContainer()->getDatabase() - ->prepare( - sprintf( - 'UPDATE %2$s SET %1$s=? WHERE id=%3$s', - $this->getColName(), - $this->getMetaModel()->getTableName(), - $id - ) - ) - ->execute($varData); + foreach ($arrValues as $id => $value) { + $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(); } } @@ -346,7 +347,7 @@ public function renameColumn($strNewColumnName) */ public function unserializeData($value) { - return deserialize($value, true); + return StringUtil::deserialize($value, true); } /** @@ -354,6 +355,6 @@ public function unserializeData($value) */ public function serializeData($value) { - return serialize($value); + return \serialize($value); } } diff --git a/src/ContaoManager/Plugin.php b/src/ContaoManager/Plugin.php new file mode 100644 index 0000000..c5f9410 --- /dev/null +++ b/src/ContaoManager/Plugin.php @@ -0,0 +1,52 @@ + + * @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\ContaoManager; + +use Contao\ManagerPlugin\Bundle\BundlePluginInterface; +use Contao\ManagerPlugin\Bundle\Config\BundleConfig; +use Contao\ManagerPlugin\Bundle\Parser\ParserInterface; +use MetaModels\AttributeFileBundle\MetaModelsAttributeFileBundle; +use MetaModels\ContaoFrontendEditingBundle\MetaModelsContaoFrontendEditingBundle; +use MetaModels\CoreBundle\MetaModelsCoreBundle; + +/** + * Plugin for the Contao Manager. + */ +class Plugin implements BundlePluginInterface +{ + /** + * {@inheritdoc} + */ + public function getBundles(ParserInterface $parser): array + { + return [ + BundleConfig::create(MetaModelsAttributeFileBundle::class) + ->setLoadAfter( + [ + MetaModelsCoreBundle::class, + MetaModelsContaoFrontendEditingBundle::class + ] + ) + ->setReplace(['metamodelsattribute_file']) + ]; + } +} diff --git a/src/MetaModels/DcGeneral/AttributeFileDefinition.php b/src/DcGeneral/AttributeFileDefinition.php similarity index 76% rename from src/MetaModels/DcGeneral/AttributeFileDefinition.php rename to src/DcGeneral/AttributeFileDefinition.php index f3ed71e..999b6c1 100644 --- a/src/MetaModels/DcGeneral/AttributeFileDefinition.php +++ b/src/DcGeneral/AttributeFileDefinition.php @@ -3,22 +3,22 @@ /** * This file is part of MetaModels/attribute_file. * - * (c) 2012-2015 The MetaModels team. + * (c) 2012-2019 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. * * This project is provided in good faith and hope to be usable by anyone. * - * @package MetaModels - * @subpackage AttributeFile + * @package MetaModels/attribute_file * @author Christian Schiffler - * @copyright 2012-2016 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.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\DcGeneral; +namespace MetaModels\AttributeFileBundle\DcGeneral; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefinitionInterface; @@ -32,7 +32,7 @@ class AttributeFileDefinition implements DefinitionInterface * * @var string[] */ - private $fileProperties = array(); + private $fileProperties = []; /** * Add a file property. @@ -43,7 +43,7 @@ class AttributeFileDefinition implements DefinitionInterface */ public function add($filePropertyName) { - if (in_array($filePropertyName, $this->fileProperties)) { + if (\in_array($filePropertyName, $this->fileProperties)) { return; } $this->fileProperties[] = $filePropertyName; 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 new file mode 100644 index 0000000..38d6790 --- /dev/null +++ b/src/DependencyInjection/MetaModelsAttributeFileExtension.php @@ -0,0 +1,131 @@ + + * @author Sven Baumann + * @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 + */ +class MetaModelsAttributeFileExtension extends Extension +{ + /** + * {@inheritDoc} + * + * @SuppressWarnings(PHPMD.LongVariable) + */ + 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/contao/config/autoload.php b/src/EventListener/BaseListener.php similarity index 51% rename from contao/config/autoload.php rename to src/EventListener/BaseListener.php index c51b003..883613c 100644 --- a/contao/config/autoload.php +++ b/src/EventListener/BaseListener.php @@ -11,21 +11,46 @@ * This project is provided in good faith and hope to be usable by anyone. * * @package MetaModels/attribute_file - * @author Christian Schiffler * @author Stefan Heimes - * @author Andreas Isaak * @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 MetaModels\Factory; + /** - * Register the templates + * Class BaseListener */ -\Contao\TemplateLoader::addFiles( - [ - 'mm_attr_file' => 'system/modules/metamodelsattribute_file/templates', - 'mm_attr_file_alternative' => 'system/modules/metamodelsattribute_file/templates', - ] -); +class BaseListener +{ + /** + * MetaModels factory. + * + * @var Factory|null + */ + private $factory; + + /** + * Attribute constructor. + * + * @param Factory $factory MetaModels factory. + */ + public function __construct(Factory $factory) + { + $this->factory = $factory; + } + + /** + * Return the factory of MetaModels. + * + * @return Factory|null + */ + protected function getFactory() + { + return $this->factory; + } +} diff --git a/src/EventListener/BuildAttributeListener.php b/src/EventListener/BuildAttributeListener.php new file mode 100644 index 0000000..979c121 --- /dev/null +++ b/src/EventListener/BuildAttributeListener.php @@ -0,0 +1,85 @@ + + * @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\EventListener; + +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\DefaultProperty; +use MetaModels\AttributeFileBundle\Attribute\File; +use MetaModels\AttributeFileBundle\DcGeneral\AttributeFileDefinition; +use MetaModels\DcGeneral\Events\MetaModel\BuildAttributeEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; + +class BuildAttributeListener +{ + /** + * This builds the dc-general property information for the virtual file order attribute. + * + * @param BuildAttributeEvent $event The event being processed. + * + * @return void + */ + public function buildAttribute(BuildAttributeEvent $event) + { + $attribute = $event->getAttribute(); + if ( + !($attribute instanceof File) + || !$attribute->get('file_multiple') + ) { + return; + } + + $container = $event->getContainer(); + $properties = $container->getPropertiesDefinition(); + $name = $attribute->getColName(); + $nameSort = \sprintf('%s__sort', $name); + + if (!$properties->hasProperty($nameSort)) { + $properties->addProperty(new DefaultProperty($nameSort)); + } + + $properties->getProperty($nameSort) + ->setWidgetType('fileTreeOrder') + ->setLabel($nameSort) + ->setExtra(['tl_class' => 'hidden']); + + $this->addAttributeToDefinition($container, $name); + } + + /** + * 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, string $name): void + { + if (!$container->hasDefinition('metamodels.file-attributes')) { + $container->setDefinition('metamodels.file-attributes', new AttributeFileDefinition()); + } + + $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 new file mode 100644 index 0000000..b828905 --- /dev/null +++ b/src/EventListener/BuildDataDefinitionListener.php @@ -0,0 +1,69 @@ + + * @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\Factory\Event\BuildDataDefinitionEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Property; +use MetaModels\AttributeFileBundle\DcGeneral\AttributeFileDefinition; + +/** + * Class BuildDataDefinitionListener + */ +class BuildDataDefinitionListener +{ + /** + * This handles all file attributes and clones the visible conditions to reflect those of the file attribute. + * + * @param BuildDataDefinitionEvent $event The event being processed. + * + * @return void + */ + public function buildDataDefinition(BuildDataDefinitionEvent $event) + { + $container = $event->getContainer(); + if (!$container->hasDefinition('metamodels.file-attributes')) { + return; + } + // All properties... + $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)) + && ($container->getPropertiesDefinition()->hasProperty($propertyName . '__sort')) + ) { + // ... must have the order field as companion, visible only when the real property is. + $file = $legend->getProperty($propertyName); + + $legend->addProperty($order = new Property($propertyName . '__sort'), $file); + + $order->setEditableCondition($file->getEditableCondition()); + $order->setVisibleCondition($file->getVisibleCondition()); + } + } + } + } + } +} 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/MetaModels/Events/DcGeneral/Table/Attribute/File/RemoveTypeOptions.php b/src/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptions.php similarity index 64% rename from src/MetaModels/Events/DcGeneral/Table/Attribute/File/RemoveTypeOptions.php rename to src/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptions.php index 4947e65..1cfa8e2 100644 --- a/src/MetaModels/Events/DcGeneral/Table/Attribute/File/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,14 +12,16 @@ * * @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 */ -namespace MetaModels\Events\DcGeneral\Table\Attribute\File; +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/MetaModels/Events/DcGeneral/Table/FilterSetting/File/RemoveAttIdOptions.php b/src/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptions.php similarity index 64% rename from src/MetaModels/Events/DcGeneral/Table/FilterSetting/File/RemoveAttIdOptions.php rename to src/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptions.php index 71a62cb..c4da897 100644 --- a/src/MetaModels/Events/DcGeneral/Table/FilterSetting/File/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,14 +12,15 @@ * * @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 */ -namespace MetaModels\Events\DcGeneral\Table\FilterSetting\File; +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/MetaModels/Events/Attribute/File/AddAttributeInformation.php b/src/EventListener/Factory/AddAttributeInformation.php similarity index 97% rename from src/MetaModels/Events/Attribute/File/AddAttributeInformation.php rename to src/EventListener/Factory/AddAttributeInformation.php index 5f644ab..9abdc9f 100644 --- a/src/MetaModels/Events/Attribute/File/AddAttributeInformation.php +++ b/src/EventListener/Factory/AddAttributeInformation.php @@ -17,7 +17,7 @@ * @filesource */ -namespace MetaModels\Events\Attribute\File; +namespace MetaModels\AttributeFileBundle\EventListener\Factory; use MetaModels\Attribute\Events\CollectMetaModelAttributeInformationEvent; 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/MetaModels/Attribute/File/AttributeOrderTypeFactory.php b/src/MetaModels/Attribute/File/AttributeOrderTypeFactory.php deleted file mode 100644 index 3c65ccc..0000000 --- a/src/MetaModels/Attribute/File/AttributeOrderTypeFactory.php +++ /dev/null @@ -1,83 +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\Attribute\File; - -use MetaModels\Attribute\IAttributeTypeFactory; -use MetaModels\Helper\TableManipulation; - -/** - * Attribute type factory for file order attributes. - */ -class AttributeOrderTypeFactory implements IAttributeTypeFactory -{ - /** - * {@inheritDoc} - */ - public function getTypeName() - { - return 'filesort'; - } - - /** - * {@inheritDoc} - */ - public function getTypeIcon() - { - return ''; - } - - /** - * {@inheritDoc} - */ - public function createInstance($information, $metaModel) - { - try { - TableManipulation::checkColumnExists($metaModel->getTableName(), $information['colname']); - } catch (\Exception $exception) { - return null; - } - - return new FileOrder($metaModel, $information); - } - - /** - * {@inheritDoc} - */ - public function isTranslatedType() - { - return false; - } - - /** - * {@inheritDoc} - */ - public function isSimpleType() - { - return true; - } - - /** - * {@inheritDoc} - */ - public function isComplexType() - { - return false; - } -} diff --git a/src/MetaModels/Attribute/File/AttributeTypeFactory.php b/src/MetaModels/Attribute/File/AttributeTypeFactory.php deleted file mode 100644 index 61e97bd..0000000 --- a/src/MetaModels/Attribute/File/AttributeTypeFactory.php +++ /dev/null @@ -1,77 +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\Attribute\File; - -use MetaModels\Attribute\IAttributeTypeFactory; - -/** - * Attribute type factory for file attributes. - */ -class AttributeTypeFactory implements IAttributeTypeFactory -{ - /** - * {@inheritDoc} - */ - public function getTypeName() - { - return 'file'; - } - - /** - * {@inheritDoc} - */ - public function getTypeIcon() - { - return 'system/modules/metamodelsattribute_file/html/file.png'; - } - - /** - * {@inheritDoc} - */ - public function createInstance($information, $metaModel) - { - return new File($metaModel, $information); - } - - /** - * {@inheritDoc} - */ - public function isTranslatedType() - { - return false; - } - - /** - * {@inheritDoc} - */ - public function isSimpleType() - { - return false; - } - - /** - * {@inheritDoc} - */ - public function isComplexType() - { - return true; - } -} diff --git a/src/MetaModels/Attribute/File/File.php b/src/MetaModels/Attribute/File/File.php deleted file mode 100644 index 2167cc8..0000000 --- a/src/MetaModels/Attribute/File/File.php +++ /dev/null @@ -1,434 +0,0 @@ - - * @author Andreas Isaak - * @author Christopher Boelter - * @author David Greminger - * @author David Maack - * @author MrTool - * @author Oliver Hoff - * @author Stefan Heimes - * @author Marc Reimann - * @author David Molineus - * @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\Attribute\File; - -use Contao\Config; -use Contao\FilesModel; -use Contao\Validator; -use MetaModels\Attribute\BaseComplex; -use MetaModels\Helper\TableManipulation; -use MetaModels\Helper\ToolboxFile; -use MetaModels\Render\Template; - -/** - * This is the MetaModel attribute class for handling file fields. - */ -class File extends BaseComplex -{ - /** - * {@inheritdoc} - */ - public function destroyAUX() - { - 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. - if (($colName = $this->getColName()) - && $this->getDatabase()->fieldExists($colName, $metaModel, true) - ) { - TableManipulation::dropColumn($metaModel, $colName); - // Catch error message if column does not exist . - try { - TableManipulation::dropColumn($metaModel, $colName . '__sort'); - } catch (\Exception $e) { - return; - } - } - } - - /** - * {@inheritdoc} - */ - public function initializeAUX() - { - parent::initializeAUX(); - if ($colName = $this->getColName()) { - $tableName = $this->getMetaModel()->getTableName(); - TableManipulation::createColumn($tableName, $colName, 'blob NULL'); - } - } - - /** - * {@inheritdoc} - */ - public function searchFor($strPattern) - { - // Base implementation, do a simple search on given column. - $result = $this->getDatabase() - ->prepare(sprintf( - 'SELECT id - FROM %s - WHERE %s IN - (SELECT uuid FROM - %s - WHERE path - LIKE - ?)', - $this->getMetaModel()->getTableName(), - $this->getColName(), - FilesModel::getTable() - )) - ->execute(\str_replace(['*', '?'], ['%', '_'], $strPattern)); - - $arrIds = $result->fetchEach('id'); - - return $arrIds; - } - - /** - * {@inheritdoc} - */ - public function unsetDataFor($arrIds) - { - $sortProperty = $this->getMetaModel()->getAttribute($this->getColName() . '__sort'); - - $fileSortQuery = ''; - if ($sortProperty) { - $fileSortQuery = ', %2$s__sort=null'; - } - - $this->getDatabase() - ->prepare( - sprintf( - 'UPDATE %1$s SET %2$s=null' . $fileSortQuery . ' WHERE %1$s.id IN (%3$s)', - $this->getMetaModel()->getTableName(), - $this->getColName(), - $this->parameterMask($arrIds) - ) - ) - ->execute($arrIds); - } - - /** - * {@inheritdoc} - */ - public function getDataFor($arrIds) - { - $sortProperty = $this->getMetaModel()->getAttribute($this->getColName() . '__sort'); - - $fileSortQuery = ''; - if ($sortProperty) { - $fileSortQuery = ', %1$s__sort AS file_sort'; - } - - $result = $this->getDatabase() - ->prepare( - sprintf( - 'SELECT id, %1$s AS file' . $fileSortQuery . ' FROM %2$s WHERE id IN (%3$s)', - $this->getColName(), - $this->getMetaModel()->getTableName(), - $this->parameterMask($arrIds) - ) - ) - ->execute($arrIds); - - $data = array(); - while ($result->next()) { - $row = ToolboxFile::convertValuesToMetaModels(deserialize($result->file, true)); - - if ($sortProperty) { - $row['sort'] = $sorted = \deserialize($result->file_sort, true); - - foreach (ToolboxFile::convertValuesToMetaModels($sorted) as $sortedKey => $sortedValue) { - $row[$sortedKey . '_sorted'] = $sortedValue; - } - } - - $data[$result->id] = $row; - } - - return $data; - } - - /** - * This method is called to store the data for certain items to the database. - * - * @param mixed $arrValues The values to be stored into database. Mapping is item id=>value. - * - * @return void - */ - public function setDataFor($arrValues) - { - foreach ($arrValues as $id => $varData) { - if ($varData === null) { - $varData = array('bin' => array(), 'value' => array(), 'path' => array(), 'sort' => null); - } - - $files = ToolboxFile::convertValuesToDatabase($varData); - - // Check single file or multiple file. - if ($this->get('file_multiple')) { - $files = serialize($files); - } else { - $files = $files[0]; - } - - $this->getMetaModel()->getServiceContainer()->getDatabase() - ->prepare( - sprintf( - 'UPDATE %2$s SET %1$s=? WHERE id=%3$s', - $this->getColName(), - $this->getMetaModel()->getTableName(), - $id - ) - ) - ->execute($files); - } - } - - /** - * {@inheritDoc} - */ - public function getFilterOptions($idList, $usedOnly, &$arrCount = null) - { - return array(); - } - - /** - * {@inheritdoc} - */ - public function getAttributeSettingNames() - { - return \array_merge( - parent::getAttributeSettingNames(), - [ - 'file_multiple', - 'file_customFiletree', - 'file_uploadFolder', - 'file_validFileTypes', - 'file_filesOnly', - 'file_widgetMode', - 'filterable', - 'searchable', - 'mandatory', - ] - ); - } - - /** - * Take the raw data from the DB column and unserialize it. - * - * @param mixed $value The array of data from the database. - * - * @return array - */ - public function unserializeData($value) - { - return ToolboxFile::convertValuesToMetaModels(\deserialize($value, true)); - } - - /** - * Take the data from the system and serialize it for the database. - * - * @param mixed $mixValues The data to serialize. - * - * @return string An serialized array with binary data or a binary data. - */ - public function serializeData($mixValues) - { - if ($mixValues === null) { - $mixValues = ['bin' => [], 'value' => [], 'path' => []]; - } - $arrData = ToolboxFile::convertValuesToDatabase($mixValues); - - // Check single file or multiple file. - if ($this->get('file_multiple')) { - $mixValues = \serialize($arrData); - } else { - $mixValues = $arrData[0]; - } - - return $mixValues; - } - - /** - * Manipulate the field definition for custom file trees. - * - * @param array $arrFieldDef The field definition to manipulate. - * - * @return void - */ - private function handleCustomFileTree(&$arrFieldDef) - { - if (\strlen($this->get('file_uploadFolder'))) { - // Set root path of file chooser depending on contao version. - $objFile = null; - - if (Validator::isUuid($this->get('file_uploadFolder'))) { - $objFile = FilesModel::findByUuid($this->get('file_uploadFolder')); - } - - // Check if we have a file. - if ($objFile != null) { - $arrFieldDef['eval']['path'] = $objFile->path; - } else { - // Fallback. - $arrFieldDef['eval']['path'] = $this->get('file_uploadFolder'); - } - } - - if (\strlen($this->get('file_validFileTypes'))) { - $arrFieldDef['eval']['extensions'] = $this->get('file_validFileTypes'); - } - - if (\strlen($this->get('file_filesOnly'))) { - $arrFieldDef['eval']['filesOnly'] = true; - } - } - - /** - * {@inheritdoc} - */ - public function getFieldDefinition($arrOverrides = []) - { - $arrFieldDef = parent::getFieldDefinition($arrOverrides); - - $arrFieldDef['inputType'] = 'fileTree'; - $arrFieldDef['eval']['files'] = true; - $arrFieldDef['eval']['extensions'] = Config::get('allowedDownload'); - $arrFieldDef['eval']['multiple'] = (bool) $this->get('file_multiple'); - - $widgetMode = $this->getOverrideValue('file_widgetMode', $arrOverrides); - - if (('normal' !== $widgetMode) - && ((bool) $this->get('file_multiple')) - ) { - $arrFieldDef['eval']['orderField'] = $this->getColName() . '__sort'; - } - - $arrFieldDef['eval']['isDownloads'] = ('downloads' === $widgetMode); - $arrFieldDef['eval']['isGallery'] = ('gallery' === $widgetMode); - - if ($this->get('file_multiple')) { - $arrFieldDef['eval']['fieldType'] = 'checkbox'; - } else { - $arrFieldDef['eval']['fieldType'] = 'radio'; - } - - if ($this->get('file_customFiletree')) { - $this->handleCustomFileTree($arrFieldDef); - } - - return $arrFieldDef; - } - - /** - * {@inheritdoc} - */ - public function valueToWidget($varValue) - { - if (empty($varValue)) { - return null; - } - - if (!$this->get('file_multiple')) { - return isset($varValue['bin'][0]) ? $varValue['bin'][0] : null; - } - - return $varValue['bin']; - } - - /** - * {@inheritdoc} - */ - public function widgetToValue($varValue, $itemId) - { - return ToolboxFile::convertUuidsOrPathsToMetaModels((array) $varValue); - } - - /** - * {@inheritDoc} - */ - protected function prepareTemplate(Template $objTemplate, $arrRowData, $objSettings) - { - parent::prepareTemplate($objTemplate, $arrRowData, $objSettings); - - // No data, nothing to do. - if (!$arrRowData[$this->getColName()]) { - return; - } - - $objToolbox = new ToolboxFile(); - - $objToolbox->setBaseLanguage($this->getMetaModel()->getActiveLanguage()); - - $objToolbox->setFallbackLanguage($this->getMetaModel()->getFallbackLanguage()); - - $objToolbox->setLightboxId(\sprintf( - '%s.%s.%s', - $this->getMetaModel()->getTableName(), - $objSettings->get('id'), - $arrRowData['id'] - )); - - if (\strlen($this->get('file_validFileTypes'))) { - $objToolbox->setAcceptedExtensions($this->get('file_validFileTypes')); - } - - $objToolbox->setShowImages($objSettings->get('file_showImage')); - - if ($objSettings->get('file_imageSize')) { - $objToolbox->setResizeImages($objSettings->get('file_imageSize')); - } - - $value = $arrRowData[$this->getColName()]; - - if (isset($value['value'])) { - foreach ($value['value'] as $strFile) { - $objToolbox->addPathById($strFile); - } - } elseif (\is_array($value)) { - foreach ($value as $strFile) { - $objToolbox->addPathById($strFile); - } - } else { - $objToolbox->addPathById($value); - } - - $objToolbox->resolveFiles(); - $arrData = $objToolbox->sortFiles( - $objSettings->get('file_sortBy'), - isset($value['bin_sorted']) ? $value['bin_sorted'] : [] - ); - - $objTemplate->files = $arrData['files']; - $objTemplate->src = $arrData['source']; - } - - /** - * Retrieve the database instance. - * - * @return \Contao\Database - */ - private function getDatabase() - { - return $this->getMetaModel()->getServiceContainer()->getDatabase(); - } -} diff --git a/src/MetaModels/Attribute/File/Helper/UpgradeHandler.php b/src/MetaModels/Attribute/File/Helper/UpgradeHandler.php deleted file mode 100644 index c39a563..0000000 --- a/src/MetaModels/Attribute/File/Helper/UpgradeHandler.php +++ /dev/null @@ -1,94 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2017 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0 - * @filesource - */ - -namespace MetaModels\Attribute\File\Helper; - -use Contao\Database; - -/** - * 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 Database - */ - private $database; - - /** - * Create a new instance. - * - * @param Database $database The database instance to use. - */ - public function __construct(Database $database) - { - $this->database = $database; - } - - /** - * Perform all upgrade steps. - * - * @return void - */ - public function perform() - { - $this->ensureOrderColumnExists(); - } - - /** - * Ensure that the order column exists. - * - * @return void - */ - private function ensureOrderColumnExists() - { - $attributes = $this - ->database - ->prepare( - 'SELECT metamodel.tableName, attribute.colname - FROM tl_metamodel_attribute AS attribute - LEFT JOIN tl_metamodel AS metamodel - ON (metamodel.id=attribute.pid) - WHERE attribute.type=? - AND attribute.file_multiple=?' - ) - ->execute('file', 1); - - while ($attributes->next()) { - if ($this->database->fieldExists($attributes->colname . '__sort', $attributes->tableName, true)) { - continue; - } - $this - ->database - ->execute( - sprintf( - 'ALTER TABLE %1$s ADD COLUMN %2$s__sort %3$s', - $attributes->tableName, - $attributes->colname, - 'blob NULL' - ) - ); - } - } -} diff --git a/src/MetaModels/Attribute/File/Subscriber.php b/src/MetaModels/Attribute/File/Subscriber.php deleted file mode 100644 index 02fe076..0000000 --- a/src/MetaModels/Attribute/File/Subscriber.php +++ /dev/null @@ -1,184 +0,0 @@ - - * @author David Molineus - * @author Sven Baumann - * @copyright 2012-2017 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0 - * @filesource - */ - -namespace MetaModels\Attribute\File; - -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\DefaultProperty; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Property; -use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; -use ContaoCommunityAlliance\DcGeneral\Factory\Event\BuildDataDefinitionEvent; -use MetaModels\DcGeneral\AttributeFileDefinition; -use MetaModels\DcGeneral\DataDefinition\IMetaModelDataDefinition; -use MetaModels\DcGeneral\Events\BaseSubscriber; -use MetaModels\DcGeneral\Events\MetaModel\BuildAttributeEvent; -use MetaModels\Helper\TableManipulation; - -/** - * Subscriber integrates file attribute related listeners. - * - * @package MetaModels\Attribute\File - */ -class Subscriber extends BaseSubscriber -{ - /** - * {@inheritdoc} - */ - public function registerEventsInDispatcher() - { - $this - ->addListener( - BuildAttributeEvent::NAME, - array($this, 'buildAttribute') - ) - ->addListener( - BuildDataDefinitionEvent::NAME, - array($this, 'buildDataDefinition'), - // Ensure to be after MetaModels\DcGeneral\Dca\Builder\Builder::PRIORITY (currently 50). - 0 - ) - ->addListener( - PostPersistModelEvent::NAME, - array($this, 'handleUpdateAttribute'), - -1 - ); - } - - /** - * This builds the dc-general property information for the virtual file order attribute. - * - * @param BuildAttributeEvent $event The event being processed. - * - * @return void - */ - public function buildAttribute(BuildAttributeEvent $event) - { - $attribute = $event->getAttribute(); - - if (!($attribute instanceof File) - || !$attribute->get('file_multiple') - ) { - return; - } - - $container = $event->getContainer(); - $properties = $container->getPropertiesDefinition(); - $name = $attribute->getColName(); - - if ($properties->hasProperty($name . '__sort')) { - $this->addAttributeToDefinition($container, $name); - - $properties->getProperty($name . '__sort')->setWidgetType('fileTreeOrder'); - - return; - } - - $properties->addProperty($property = new DefaultProperty($name . '__sort')); - $property->setWidgetType('fileTreeOrder'); - - $this->addAttributeToDefinition($container, $name); - } - - /** - * This handles all file attributes and clones the visible conditions to reflect those of the file attribute. - * - * @param BuildDataDefinitionEvent $event The event being processed. - * - * @return void - */ - public function buildDataDefinition(BuildDataDefinitionEvent $event) - { - $container = $event->getContainer(); - if (!$container->hasDefinition('metamodels.file-attributes')) { - return; - } - // All properties... - foreach ($container->getDefinition('metamodels.file-attributes')->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)) - && ($container->getPropertiesDefinition()->hasProperty($propertyName . '__sort')) - ) { - // ... must have the order field as companion, visible only when the real property is. - $file = $legend->getProperty($propertyName); - - $legend->addProperty($order = new Property($propertyName . '__sort'), $file); - - $order->setEditableCondition($file->getEditableCondition()); - $order->setVisibleCondition($file->getVisibleCondition()); - } - } - } - } - } - - /** - * 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 (($model->getProperty('type') !== 'file') - || (!$model->getProperty('file_multiple')) - || ($event->getEnvironment()->getDataDefinition()->getName() !== 'tl_metamodel_attribute') - ) { - return; - } - - $metaModel = $this->getMetaModelById($model->getProperty('pid')); - - $attributeName = $model->getProperty('colname') . '__sort'; - - try { - TableManipulation::checkColumnExists($metaModel->getTableName(), $attributeName); - } catch (\Exception $e) { - TableManipulation::createColumn($metaModel->getTableName(), $attributeName, 'blob NULL'); - } - } - - /** - * Add attribute to metamodels file attributes definition. - * - * @param IMetaModelDataDefinition $container The metamodel data definition. - * - * @param string $name The attribute name. - * - * @return void - */ - protected function addAttributeToDefinition(IMetaModelDataDefinition $container, $name) - { - if (!$container->hasDefinition('metamodels.file-attributes')) { - $container->setDefinition('metamodels.file-attributes', new AttributeFileDefinition()); - } - - $container->getDefinition('metamodels.file-attributes')->add($name); - } -} diff --git a/src/MetaModels/Events/Attribute/File/ImageSizeOptions.php b/src/MetaModels/Events/Attribute/File/ImageSizeOptions.php deleted file mode 100644 index 467ebc1..0000000 --- a/src/MetaModels/Events/Attribute/File/ImageSizeOptions.php +++ /dev/null @@ -1,88 +0,0 @@ - - * @author Stefan Heimes - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/attribute_file/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace MetaModels\Events\Attribute\File; - -use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; -use ContaoCommunityAlliance\DcGeneral\Data\DefaultDataProvider; - -/** - * Get the options for the image size. - */ -class ImageSizeOptions -{ - /** - * 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/contao/dca/tl_metamodel_dcasetting.php b/src/MetaModelsAttributeFileBundle.php similarity index 67% rename from contao/dca/tl_metamodel_dcasetting.php rename to src/MetaModelsAttributeFileBundle.php index 99e4ec7..3208f33 100644 --- a/contao/dca/tl_metamodel_dcasetting.php +++ b/src/MetaModelsAttributeFileBundle.php @@ -12,19 +12,19 @@ * * @package MetaModels/attribute_file * @author Christian Schiffler - * @author Stefan Heimes - * @author Andreas Isaak * @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 */ -$GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['metasubselectpalettes']['attr_id']['file'] = [ - 'presentation' => [ - 'tl_class' - ], - 'functions' => [ - 'mandatory' - ] -]; +namespace MetaModels\AttributeFileBundle; + +use Symfony\Component\HttpKernel\Bundle\Bundle; + +/** + * This is the bundle class. + */ +class MetaModelsAttributeFileBundle extends Bundle +{ +} 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 new file mode 100644 index 0000000..8ab2146 --- /dev/null +++ b/src/Resources/config/event_listener.yml @@ -0,0 +1,79 @@ +services: + 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 new file mode 100644 index 0000000..fe9fcaa --- /dev/null +++ b/src/Resources/config/factory.yml @@ -0,0 +1,30 @@ +services: + metamodels.attribute_file.factory: + class: MetaModels\AttributeFileBundle\Attribute\AttributeTypeFactory + arguments: + - "@database_connection" + - "@metamodels.table_manipulator" + - '@MetaModels\Helper\ToolboxFile' + - "@=service('contao.framework').getAdapter('Contao\\\\StringUtil')" + - "@=service('contao.framework').getAdapter('Contao\\\\Validator')" + - "@=service('contao.framework').getAdapter('Contao\\\\FilesModel')" + - "@=service('contao.framework').getAdapter('Contao\\\\Config')" + tags: + - { name: metamodels.attribute_factory } + + metamodels.attribute_file_order.factory: + class: MetaModels\AttributeFileBundle\Attribute\AttributeOrderTypeFactory + arguments: + - "@database_connection" + - "@metamodels.table_manipulator" + tags: + - { name: metamodels.attribute_factory } + + metamodels.attribute_file.event_listener_factory.add_attribute_information: + class: MetaModels\AttributeFileBundle\EventListener\Factory\AddAttributeInformation + public: true + tags: + - name: kernel.event_listener + event: metamodels.metamodel.collect-metamodel-attribute-information + method: addInformation + priority: -1 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 new file mode 100644 index 0000000..9c59da6 --- /dev/null +++ b/src/Resources/config/services.yml @@ -0,0 +1,34 @@ +services: + metamodels.attribute_file.toolbox.file: + 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: + - "%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/dca/tl_metamodel_attribute.php b/src/Resources/contao/dca/tl_metamodel_attribute.php new file mode 100644 index 0000000..32a4fa3 --- /dev/null +++ b/src/Resources/contao/dca/tl_metamodel_attribute.php @@ -0,0 +1,80 @@ + + * @author Stefan Heimes + * @author Andreas Isaak + * @author David Molineus + * @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 + */ + +$GLOBALS['TL_DCA']['tl_metamodel_attribute']['metapalettes']['file extends _simpleattribute_'] = [ + '+advanced' => ['file_customFiletree', 'file_multiple'] +]; + +$GLOBALS['TL_DCA']['tl_metamodel_attribute']['metasubpalettes']['file_customFiletree'] = [ + 'file_uploadFolder', + 'file_validFileTypes', + 'file_filesOnly' +]; + +$GLOBALS['TL_DCA']['tl_metamodel_attribute']['fields']['file_customFiletree'] = [ + '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' => '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' => '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' => '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' => '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 new file mode 100644 index 0000000..ba69c80 --- /dev/null +++ b/src/Resources/contao/dca/tl_metamodel_dcasetting.php @@ -0,0 +1,252 @@ + + * @author Stefan Heimes + * @author Andreas Isaak + * @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 + */ + +use Contao\BackendUser; +use Contao\System; +use MetaModels\ContaoFrontendEditingBundle\MetaModelsContaoFrontendEditingBundle; + +$GLOBALS['TL_DCA']['tl_metamodel_dcasetting']['metasubselectpalettes']['attr_id']['file'] = [ + 'presentation' => [ + 'tl_class', + 'be_template', + ], + 'functions' => [ + '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 new file mode 100644 index 0000000..8eb74b0 --- /dev/null +++ b/src/Resources/contao/dca/tl_metamodel_rendersetting.php @@ -0,0 +1,124 @@ + + * @author Andreas Isaak + * @author Sven Baumann + * @author Ingolf Steinhardt + * @author Stefan Heimes + * @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_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_placeholder' +]; + +$GLOBALS['TL_DCA']['tl_metamodel_rendersetting']['fields']['file_sortBy'] = [ + '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' => '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' => 'file_showImage.label', + 'description' => 'file_showImage.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_imageSize'] = [ + 'label' => 'file_imageSize.label', + 'description' => 'file_imageSize.description', + 'exclude' => true, + 'inputType' => 'imageSize', + 'eval' => [ + 'rgxp' => 'natural', + 'includeBlankOption' => true, + 'nospace' => true, + '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/templates/mm_attr_file.html5 b/src/Resources/contao/templates/mm_attr_file.html5 new file mode 100644 index 0000000..e4a31b3 --- /dev/null +++ b/src/Resources/contao/templates/mm_attr_file.html5 @@ -0,0 +1,46 @@ +src)): ?> + + diff --git a/src/Resources/contao/templates/mm_attr_file.text b/src/Resources/contao/templates/mm_attr_file.text new file mode 100644 index 0000000..07cd8b8 --- /dev/null +++ b/src/Resources/contao/templates/mm_attr_file.text @@ -0,0 +1 @@ +files)) { echo implode(',', (array)$this->files); } ?> diff --git a/src/Resources/contao/templates/mm_attr_file_alternative.html5 b/src/Resources/contao/templates/mm_attr_file_alternative.html5 new file mode 100644 index 0000000..136da9c --- /dev/null +++ b/src/Resources/contao/templates/mm_attr_file_alternative.html5 @@ -0,0 +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')): ?> + +
+ +
+ +
+ + diff --git a/src/Resources/contao/templates/mm_attr_file_alternative.text b/src/Resources/contao/templates/mm_attr_file_alternative.text new file mode 100644 index 0000000..9c95fd1 --- /dev/null +++ b/src/Resources/contao/templates/mm_attr_file_alternative.text @@ -0,0 +1,3 @@ +files as $file): ?> + + diff --git a/contao/html/file.png b/src/Resources/public/file.png similarity index 100% rename from contao/html/file.png rename to src/Resources/public/file.png 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 new file mode 100644 index 0000000..7a050dc --- /dev/null +++ b/src/deprecated-autoload.php @@ -0,0 +1,51 @@ + + * @author Stefan Heimes + * @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 + */ + +use MetaModels\AttributeFileBundle\Attribute\AttributeTypeFactory; +use MetaModels\AttributeFileBundle\Attribute\File; +use MetaModels\AttributeFileBundle\Attribute\FileOrder; +use MetaModels\AttributeFileBundle\DcGeneral\AttributeFileDefinition; +use MetaModels\AttributeFileBundle\EventListener\ImageSizeOptionsProvider; + +// This hack is to load the "old locations" of the classes. +spl_autoload_register( + function ($class) { + static $classes = [ + 'MetaModels\Attribute\File\File' => File::class, + 'MetaModels\Attribute\File\FileOrder' => FileOrder::class, + 'MetaModels\Attribute\File\AttributeTypeFactory' => AttributeTypeFactory::class, + 'MetaModels\DcGeneral\AttributeFileDefinition' => AttributeFileDefinition::class, + 'MetaModels\Events\Attribute\File\ImageSizeOptions' => ImageSizeOptionsProvider::class + ]; + + if (isset($classes[$class])) { + // @codingStandardsIgnoreStart Silencing errors is discouraged + @trigger_error('Class "' . $class . '" has been renamed to "' . $classes[$class] . '"', E_USER_DEPRECATED); + // @codingStandardsIgnoreEnd + + if (!class_exists($classes[$class])) { + spl_autoload_call($class); + } + + class_alias($classes[$class], $class); + } + } +); diff --git a/tests/Attribute/FileAttributeTypeFactoryTest.php b/tests/Attribute/FileAttributeTypeFactoryTest.php new file mode 100644 index 0000000..a5d1bd1 --- /dev/null +++ b/tests/Attribute/FileAttributeTypeFactoryTest.php @@ -0,0 +1,246 @@ + + * @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 + */ + +namespace MetaModels\AttributeFileBundle\Test\Attribute; + +use Contao\CoreBundle\Framework\Adapter; +use Contao\CoreBundle\Image\ImageFactoryInterface; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use MetaModels\Attribute\IAttributeTypeFactory; +use MetaModels\AttributeFileBundle\Attribute\AttributeOrderTypeFactory; +use MetaModels\AttributeFileBundle\Attribute\AttributeTypeFactory; +use MetaModels\AttributeFileBundle\Attribute\File; +use MetaModels\AttributeFileBundle\Attribute\FileOrder; +use MetaModels\Helper\TableManipulator; +use MetaModels\Helper\ToolboxFile; +use MetaModels\IMetaModel; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * Test the attribute factory. + * + * @covers \MetaModels\AttributeFileBundle\Attribute\AttributeTypeFactory + * @covers \MetaModels\AttributeFileBundle\Attribute\AttributeOrderTypeFactory + */ +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 + */ + protected function mockMetaModel($tableName, $language, $fallbackLanguage) + { + $metaModel = $this->getMockForAbstractClass(IMetaModel::class); + + $metaModel + ->expects(self::any()) + ->method('getTableName') + ->willReturn($tableName); + + $metaModel + ->expects(self::any()) + ->method('getActiveLanguage') + ->willReturn($language); + + $metaModel + ->expects(self::any()) + ->method('getFallbackLanguage') + ->willReturn($fallbackLanguage); + + return $metaModel; + } + + /** + * Mock the database connection. + * + * @return MockObject|Connection + */ + private function mockConnection(AbstractSchemaManager $schemaManager = null) + { + $connection = $this->getMockBuilder(Connection::class) + ->disableOriginalConstructor() + ->getMock(); + + $connection + ->expects(self::any()) + ->method('createSchemaManager') + ->willReturn($schemaManager ?? $this->mockSchemaManager()); + + return $connection; + } + + private function mockSchemaManager(array $tableSchema = []) + { + $manager = $this->getMockForAbstractClass( + AbstractSchemaManager::class, + [], + '', + false, + true, + true, + ['listTableColumns'] + ); + + $manager + ->expects(self::any()) + ->method('listTableColumns') + ->willReturnCallback( + function ($table) use ($tableSchema) { + return $tableSchema[$table] ?? null; + } + ); + + return $manager; + } + + /** + * Mock the table manipulator. + * + * @param Connection $connection The database connection mock. + * + * @return TableManipulator|MockObject + */ + private function mockTableManipulator(Connection $connection) + { + return $this->getMockBuilder(TableManipulator::class) + ->setConstructorArgs([$connection, []]) + ->getMock(); + } + + private function mockToolboxFile() + { + return $this + ->getMockBuilder(ToolboxFile::class) + ->disableOriginalConstructor() + ->getMock(); + } + + private function mockStringUtil() + { + return $this + ->getMockBuilder(Adapter::class) + ->disableOriginalConstructor() + ->getMock(); + } + + private function mockValidator() + { + return $this + ->getMockBuilder(Adapter::class) + ->disableOriginalConstructor() + ->getMock(); + } + + private function mockFileRepository() + { + return $this + ->getMockBuilder(Adapter::class) + ->disableOriginalConstructor() + ->getMock(); + } + + private function mockConfig() + { + return $this + ->getMockBuilder(Adapter::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Override the method to run the tests on the attribute factories to be tested. + * + * @return IAttributeTypeFactory[] + */ + protected function getAttributeOrderFactories($connection, $tableManipulator) + { + return [new AttributeOrderTypeFactory($connection, $tableManipulator)]; + } + + /** + * Test creation of a file attribute. + * + * @return void + */ + public function testCreateFile() + { + $connection = $this->mockConnection(); + $manipulator = $this->mockTableManipulator($connection); + + $factory = new AttributeTypeFactory( + $connection, + $manipulator, + $this->mockToolboxFile(), + $this->mockStringUtil(), + $this->mockValidator(), + $this->mockFileRepository(), + $this->mockConfig() + ); + + $values = [ + 'colname' => 'test' + ]; + $attribute = $factory->createInstance( + $values, + $this->mockMetaModel('mm_test', 'de', 'en') + ); + + self::assertInstanceOf(File::class, $attribute); + + foreach ($values as $key => $value) { + self::assertEquals($value, $attribute->get($key), $key); + } + } + + /** + * Test creation of a file attribute. + * + * @return void + */ + public function testCreateOrderSelect() + { + $tableSchema = [ + 'mm_test' => [ + 'test__sort' => '' + ] + ]; + + $connection = $this->mockConnection($this->mockSchemaManager($tableSchema)); + $manipulator = $this->mockTableManipulator($connection); + + $factory = new AttributeOrderTypeFactory($connection, $manipulator); + $values = [ + 'colname' => 'test__sort' + ]; + $attribute = $factory->createInstance( + $values, + $this->mockMetaModel('mm_test', 'de', 'en') + ); + + self::assertInstanceOf(FileOrder::class, $attribute); + } +} diff --git a/tests/Attribute/FileTest.php b/tests/Attribute/FileTest.php new file mode 100644 index 0000000..d434ff5 --- /dev/null +++ b/tests/Attribute/FileTest.php @@ -0,0 +1,446 @@ + + * @author David Greminger + * @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\Attribute; + +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\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 +{ + /** + * Mock a MetaModel. + * + * @param string $tableName The table name. + * @param string $language The language. + * + * @return IMetaModel + */ + protected function mockMetaModel($tableName, $language) + { + $metaModel = $this->getMockForAbstractClass(IMetaModel::class); + + $metaModel + ->expects(self::any()) + ->method('getTableName') + ->willReturn($tableName); + + $metaModel + ->expects(self::any()) + ->method('getActiveLanguage') + ->willReturn($language); + + return $metaModel; + } + + /** + * Mock the database connection. + * + * @param array $methods The method names to mock. + * + * @return MockObject|Connection + */ + private function mockConnection($methods = []) + { + $connection = $this->getMockBuilder(Connection::class) + ->disableOriginalConstructor() + ->onlyMethods(\array_merge($methods, ['getDatabasePlatform'])) + ->getMock(); + + $platform = $this + ->getMockBuilder(AbstractPlatform::class) + ->disableOriginalConstructor() + ->onlyMethods([]) + ->getMockForAbstractClass(); + $connection->method('getDatabasePlatform')->willReturn($platform); + + return $connection; + } + + /** + * Mock the table manipulator. + * + * @param Connection $connection The database connection mock. + * + * @return TableManipulator|MockObject + */ + private function mockTableManipulator(Connection $connection) + { + return $this->getMockBuilder(TableManipulator::class) + ->setConstructorArgs([$connection, []]) + ->getMock(); + } + + /** + * Mock the image factory. + * + * @return ImageFactoryInterface|MockObject + * + * @SuppressWarnings(PHPMD.UnusedPrivateMethod) + */ + private function mockImageFactory() + { + return $this->getMockBuilder(ImageFactoryInterface::class) + ->getMockForAbstractClass(); + } + + private function mockToolboxFile() + { + $toolbox = $this + ->getMockBuilder(ToolboxFile::class) + ->disableOriginalConstructor() + ->getMock(); + + return $toolbox; + } + + private function mockStringUtil() + { + return $this + ->getMockBuilder(Adapter::class) + ->disableOriginalConstructor() + ->getMock(); + } + + private function mockValidator() + { + return $this + ->getMockBuilder(Adapter::class) + ->disableOriginalConstructor() + ->getMock(); + } + + private function mockFileRepository() + { + return $this + ->getMockBuilder(Adapter::class) + ->disableOriginalConstructor() + ->getMock(); + } + + private function mockConfig() + { + return $this + ->getMockBuilder(Adapter::class) + ->disableOriginalConstructor() + ->getMock(); + } + + /** + * Test that the attribute can be instantiated. + * + * @return void + */ + public function testInstantiation() + { + $metaModel = $this->mockMetaModel('en', 'en'); + $connection = $this->mockConnection(); + $manipulator = $this->mockTableManipulator($connection); + + $file = new File( + $metaModel, + [], + $connection, + $manipulator, + $this->mockToolboxFile(), + $this->mockStringUtil(), + $this->mockValidator(), + $this->mockFileRepository(), + $this->mockConfig() + ); + + self::assertInstanceOf(File::class, $file); + } + + /** + * Test that empty values are handled correctly. + * + * @return void + */ + public function testEmptyValues() + { + $metaModel = $this->mockMetaModel('en', 'en'); + $connection = $this->mockConnection(); + $manipulator = $this->mockTableManipulator($connection); + + $file = new File( + $metaModel, + ['file_multiple' => false], + $connection, + $manipulator, + $this->mockToolboxFile(), + $this->mockStringUtil(), + $this->mockValidator(), + $this->mockFileRepository(), + $this->mockConfig() + ); + + self::assertEquals( + ['bin' => [], 'value' => [], 'path' => [], 'meta' => []], + $file->widgetToValue(null, 1) + ); + self::assertEquals( + ['bin' => [], 'value' => [], 'path' => [], 'meta' => []], + $file->widgetToValue([], 1) + ); + } + + /** + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) + */ + public function testSearchForFileName() + { + $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); + + $connection + ->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')); + + 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() + ->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'), + ], + ] + ); + + $builder1 = $this + ->getMockBuilder(QueryBuilder::class) + ->setConstructorArgs([$connection]) + ->onlyMethods(['executeQuery']) + ->getMock(); + + $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); + + $connection + ->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('*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() + ); + + 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() + ); + + 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 new file mode 100644 index 0000000..98c1532 --- /dev/null +++ b/tests/ContaoManager/PluginTest.php @@ -0,0 +1,74 @@ + + * @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 + */ + +namespace MetaModels\AttributeFileBundle\Test\ContaoManager; + +use Contao\ManagerPlugin\Bundle\BundlePluginInterface; +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 +{ + /** + * Test that plugin can be instantiated. + * + * @return void + */ + public function testInstantiation() + { + $plugin = new Plugin(); + + self::assertInstanceOf(Plugin::class, $plugin); + self::assertInstanceOf(BundlePluginInterface::class, $plugin); + } + + /** + * Tests that the a valid bundle config is created. + * + * @return void + */ + public function testBundleConfig() + { + $parser = $this->getMockBuilder(ParserInterface::class)->getMock(); + $plugin = new Plugin(); + $bundles = $plugin->getBundles($parser); + + self::assertContainsOnlyInstancesOf(BundleConfig::class, $bundles); + self::assertCount(1, $bundles); + + /** @var BundleConfig $bundleConfig */ + $bundleConfig = $bundles[0]; + + 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 new file mode 100644 index 0000000..02b795d --- /dev/null +++ b/tests/DependencyInjection/MetaModelsAttributeFileExtensionTest.php @@ -0,0 +1,89 @@ + + * @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\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; +use Symfony\Component\DependencyInjection\Extension\ExtensionInterface; + +/** + * This test case test the extension. + * + * @covers \MetaModels\AttributeFileBundle\DependencyInjection\MetaModelsAttributeFileExtension + */ +class MetaModelsAttributeFileExtensionTest extends TestCase +{ + public function testInstantiation(): void + { + $extension = new MetaModelsAttributeFileExtension(); + + self::assertInstanceOf(MetaModelsAttributeFileExtension::class, $extension); + self::assertInstanceOf(ExtensionInterface::class, $extension); + } + + public function testFactoryIsRegistered(): void + { + $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 new file mode 100644 index 0000000..c2302fb --- /dev/null +++ b/tests/DeprecatedAutoloaderTest.php @@ -0,0 +1,88 @@ + + * @author Stefan Heimes + * @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\Test; + +use MetaModels\AttributeFileBundle\Attribute\File; +use MetaModels\AttributeFileBundle\Attribute\AttributeTypeFactory; +use MetaModels\AttributeFileBundle\Attribute\FileOrder; +use MetaModels\AttributeFileBundle\DcGeneral\AttributeFileDefinition; +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 +{ + /** + * Selects of old classes to the new one. + * + * @var array + */ + private static $classes = [ + 'MetaModels\Attribute\File\File' => File::class, + 'MetaModels\Attribute\File\FileOrder' => FileOrder::class, + 'MetaModels\Attribute\File\AttributeTypeFactory' => AttributeTypeFactory::class, + 'MetaModels\DcGeneral\AttributeFileDefinition' => AttributeFileDefinition::class, + 'MetaModels\Events\Attribute\File\ImageSizeOptions' => ImageSizeOptionsProvider::class, + ]; + + /** + * Provide the alias class map. + * + * @return array + */ + public function provideAliasClassMap() + { + $values = []; + + foreach (static::$classes as $select => $class) { + $values[] = [$select, $class]; + } + + return $values; + } + + /** + * Test if the deprecated classes are aliased to the new one. + * + * @param string $oldClass Old class name. + * @param string $newClass New class name. + * + * @dataProvider provideAliasClassMap + */ + public function testDeprecatedClassesAreAliased($oldClass, $newClass) + { + self::assertTrue(\class_exists($oldClass), \sprintf('Class select "%s" is not found.', $oldClass)); + + $oldClassReflection = new \ReflectionClass($oldClass); + $newClassReflection = new \ReflectionClass($newClass); + + self::assertSame($newClassReflection->getFileName(), $oldClassReflection->getFileName()); + } +} diff --git a/tests/MetaModels/Test/Events/DcGeneral/Table/Attribute/File/RemoveTypeOptionsTest.php b/tests/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptionsTest.php similarity index 73% rename from tests/MetaModels/Test/Events/DcGeneral/Table/Attribute/File/RemoveTypeOptionsTest.php rename to tests/EventListener/DcGeneral/Table/Attribute/RemoveTypeOptionsTest.php index 49169be..5144ef2 100644 --- a/tests/MetaModels/Test/Events/DcGeneral/Table/Attribute/File/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,25 +12,25 @@ * * @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 */ -namespace MetaModels\Test\Events\DcGeneral\Table\Attribute\File; +namespace MetaModels\AttributeFileBundle\Test\EventListener\DcGeneral\Table\Attribute; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; -use MetaModels\Events\DcGeneral\Table\Attribute\File\RemoveTypeOptions; +use MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\Attribute\RemoveTypeOptions; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; /** * This test the event listener. * - * @covers \MetaModels\Events\DcGeneral\Table\Attribute\File\RemoveTypeOptions + * @covers \MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\Attribute\RemoveTypeOptions */ class RemoveTypeOptionsTest extends TestCase { @@ -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/MetaModels/Test/Events/DcGeneral/Table/FilterSetting/File/RemoveAttIdOptionsTest.php b/tests/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptionsTest.php similarity index 65% rename from tests/MetaModels/Test/Events/DcGeneral/Table/FilterSetting/File/RemoveAttIdOptionsTest.php rename to tests/EventListener/DcGeneral/Table/FilterSetting/RemoveAttIdOptionsTest.php index 1bc0681..0d165d4 100644 --- a/tests/MetaModels/Test/Events/DcGeneral/Table/FilterSetting/File/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,25 +12,26 @@ * * @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 */ -namespace MetaModels\Test\Events\DcGeneral\Table\FilterSetting\File; +namespace MetaModels\AttributeFileBundle\Test\EventListener\DcGeneral\Table\FilterSetting; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; -use MetaModels\Events\DcGeneral\Table\FilterSetting\File\RemoveAttIdOptions; +use MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\FilterSetting\RemoveAttIdOptions; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; /** * This test the event listener. * - * @covers \MetaModels\Events\DcGeneral\Table\FilterSetting\File\RemoveAttIdOptions + * @covers \MetaModels\AttributeFileBundle\EventListener\DcGeneral\Table\FilterSetting\RemoveAttIdOptions */ class RemoveAttIdOptionsTest extends TestCase { @@ -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'] + ] ]; } @@ -109,12 +116,11 @@ public function testRemoveOption(array $expected, $providerName, $propertyName, [new RemoveAttIdOptions(), 'removeOption'] ); - $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/MetaModels/Test/Events/Attribute/File/AddAttributeInformationTest.php b/tests/EventListener/Factory/AddAttributeInformationTest.php similarity index 92% rename from tests/MetaModels/Test/Events/Attribute/File/AddAttributeInformationTest.php rename to tests/EventListener/Factory/AddAttributeInformationTest.php index 9e468a7..ad0f233 100644 --- a/tests/MetaModels/Test/Events/Attribute/File/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,15 +12,16 @@ * * @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 */ -namespace MetaModels\Test\Events\Attribute\File; +namespace MetaModels\AttributeFileBundle\Test\EventListener\Factory; use MetaModels\Attribute\Events\CollectMetaModelAttributeInformationEvent; -use MetaModels\Events\Attribute\File\AddAttributeInformation; +use MetaModels\AttributeFileBundle\EventListener\Factory\AddAttributeInformation; use MetaModels\IMetaModel; use PHPUnit\Framework\TestCase; use Symfony\Component\EventDispatcher\EventDispatcher; @@ -28,10 +29,11 @@ /** * This test the event listener add attribute information. * - * @covers \MetaModels\Events\Attribute\File\AddAttributeInformation + * @covers \MetaModels\AttributeFileBundle\EventListener\Factory\AddAttributeInformation */ 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/MetaModels/Test/Attribute/File/FileAttributeTypeFactoryTest.php b/tests/MetaModels/Test/Attribute/File/FileAttributeTypeFactoryTest.php deleted file mode 100644 index 65dd78a..0000000 --- a/tests/MetaModels/Test/Attribute/File/FileAttributeTypeFactoryTest.php +++ /dev/null @@ -1,133 +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\Test\Attribute\File; - -use MetaModels\Attribute\File\AttributeOrderTypeFactory; -use MetaModels\Attribute\File\FileOrder; -use MetaModels\Attribute\IAttributeTypeFactory; -use MetaModels\Attribute\File\AttributeTypeFactory; -use MetaModels\IMetaModel; -use MetaModels\Test\Attribute\AttributeTypeFactoryTest; -use MetaModels\Attribute\File\File; - -/** - * Test the attribute factory. - * - * @covers \MetaModels\Attribute\File\AttributeTypeFactory - * @covers \MetaModels\Attribute\File\AttributeOrderTypeFactory - */ -class FileAttributeTypeFactoryTest extends AttributeTypeFactoryTest -{ - /** - * Mock a MetaModel. - * - * @param string $tableName The table name. - * - * @param string $language The language. - * - * @param string $fallbackLanguage The fallback language. - * - * @return IMetaModel - */ - protected function mockMetaModel($tableName, $language, $fallbackLanguage) - { - $metaModel = $this->getMockForAbstractClass(IMetaModel::class); - - $metaModel - ->expects($this->any()) - ->method('getTableName') - ->will($this->returnValue($tableName)); - - $metaModel - ->expects($this->any()) - ->method('getActiveLanguage') - ->will($this->returnValue($language)); - - $metaModel - ->expects($this->any()) - ->method('getFallbackLanguage') - ->will($this->returnValue($fallbackLanguage)); - - return $metaModel; - } - - /** - * Override the method to run the tests on the attribute factories to be tested. - * - * @return IAttributeTypeFactory[] - */ - protected function getAttributeFactories() - { - return [new AttributeTypeFactory()]; - } - - /** - * Override the method to run the tests on the attribute factories to be tested. - * - * @return IAttributeTypeFactory[] - */ - protected function getAttributeOrderFactories() - { - return [new AttributeOrderTypeFactory()]; - } - - /** - * Test creation of a file attribute. - * - * @return void - */ - public function testCreateSelect() - { - $factory = new AttributeTypeFactory(); - $values = [ - 'colname' => 'test' - ]; - $attribute = $factory->createInstance( - $values, - $this->mockMetaModel('mm_test', 'de', 'en') - ); - - $this->assertInstanceOf(File::class, $attribute); - - foreach ($values as $key => $value) { - $this->assertEquals($value, $attribute->get($key), $key); - } - } - - /** - * Test creation of a file attribute. - * - * @return void - */ - public function testCreateOrderSelect() - { - $factory = new AttributeOrderTypeFactory(); - $values = [ - 'colname' => 'test__sort' - ]; - $attribute = $factory->createInstance( - $values, - $this->mockMetaModel('mm_test', 'de', 'en') - ); - - $this->assertNull($attribute); - } -} diff --git a/tests/MetaModels/Test/Attribute/File/FileTest.php b/tests/MetaModels/Test/Attribute/File/FileTest.php deleted file mode 100644 index 094681e..0000000 --- a/tests/MetaModels/Test/Attribute/File/FileTest.php +++ /dev/null @@ -1,100 +0,0 @@ - - * @author David Greminger - * @author Ingolf Steinhardt - * @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\Test\Attribute\File; - -use MetaModels\Attribute\File\File; -use PHPUnit\Framework\TestCase; -use MetaModels\IMetaModel; - -/** - * Unit tests to test class File. - * - * @covers \MetaModels\Attribute\File\File - */ -class FileTest extends TestCase -{ - /** - * Mock a MetaModel. - * - * @param string $language The language. - * @param string $fallbackLanguage The fallback language. - * - * @return \MetaModels\IMetaModel - */ - protected function mockMetaModel($language, $fallbackLanguage) - { - $metaModel = $this->getMockForAbstractClass(IMetaModel::class); - - $metaModel - ->expects($this->any()) - ->method('getTableName') - ->will($this->returnValue('mm_unittest')); - - $metaModel - ->expects($this->any()) - ->method('getActiveLanguage') - ->will($this->returnValue($language)); - - $metaModel - ->expects($this->any()) - ->method('getFallbackLanguage') - ->will($this->returnValue($fallbackLanguage)); - - return $metaModel; - } - - /** - * Test that the attribute can be instantiated. - * - * @return void - */ - public function testInstantiation() - { - $text = new File($this->mockMetaModel('en', 'en')); - $this->assertInstanceOf(File::class, $text); - } - - /** - * Test that empty values are handled correctly. - * - * @return void - */ - public function testEmptyValues() - { - $file = new File( - $this->mockMetaModel('en', 'en'), - [ - 'file_multiple' => false - ] - ); - - $this->assertEquals( - ['bin' => [], 'value' => [], 'path' => [], 'meta' => []], - $file->widgetToValue(null, 1) - ); - $this->assertEquals( - ['bin' => [], 'value' => [], 'path' => [], 'meta' => []], - $file->widgetToValue([], 1) - ); - } -} 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()); + } +} diff --git a/tests/bootstrap.php b/tests/bootstrap.php deleted file mode 100644 index cd8d44a..0000000 --- a/tests/bootstrap.php +++ /dev/null @@ -1,37 +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 - */ - -error_reporting(E_ALL); - -function includeIfExists($file) -{ - return file_exists($file) ? include $file : false; -} - -if ( - // Locally installed dependencies. - (!$loader = includeIfExists(__DIR__.'/../vendor/autoload.php')) - // We are within an composer install. - && (!$loader = includeIfExists(__DIR__.'/../../../autoload.php'))) { - echo 'You must set up the project dependencies, run the following commands:'.PHP_EOL. - 'curl -sS https://getcomposer.org/installer | php'.PHP_EOL. - 'php composer.phar install'.PHP_EOL; - exit(1); -} diff --git a/travis.php.ini b/travis.php.ini new file mode 100644 index 0000000..2a30f41 --- /dev/null +++ b/travis.php.ini @@ -0,0 +1 @@ +memory_limit = 2G