diff --git a/.composer-require-checker.json b/.composer-require-checker.json new file mode 100644 index 000000000..53cf5c017 --- /dev/null +++ b/.composer-require-checker.json @@ -0,0 +1,16 @@ +{ + "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", + "Doctrine\\Bundle\\DoctrineBundle\\DoctrineBundle", + "Knp\\Menu\\FactoryInterface", + "Knp\\Menu\\ItemInterface", + "Symfony\\Bundle\\FrameworkBundle\\Translation\\Translator", + "Symfony\\Contracts\\Cache\\CacheInterface", + "tl_user_group" + ] +} diff --git a/.github/workflows/diagnostics.yml b/.github/workflows/diagnostics.yml index 38c73a3e2..c4e707777 100644 --- a/.github/workflows/diagnostics.yml +++ b/.github/workflows/diagnostics.yml @@ -1,52 +1,74 @@ name: MetaModels core - on: - push: - branches-ignore: - - '**-translation' pull_request: + push: + branches: jobs: build: runs-on: ubuntu-latest + name: 'PHP: ${{ matrix.php }} Contao: ${{ matrix.contao }}' strategy: + fail-fast: false matrix: - php: [7.4] - contao: [~4.9.0] + php: [ '8.1', '8.2' ] + contao: [ '~4.13.0' ] + phpcq_install: [ 'update' ] + output: [ '-o github-action -o default' ] steps: - - name: PHP ${{ matrix.php }} ${{ matrix.contao }} Pull source + - name: Pull source uses: actions/checkout@v3 - with: - fetch-depth: 0 - # see https://github.com/shivammathur/setup-php - - name: PHP ${{ matrix.php }} ${{ matrix.contao }} Setup PHP. + - name: Setup PHP with PECL extension uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} - coverage: none - - name: PHP ${{ matrix.php }} ${{ matrix.contao }} Cache composer cache directory + # setup caches + - name: Cache composer cache directory uses: actions/cache@v3 env: cache-name: composer-cache-dir with: path: ~/.cache/composer - key: ${{ runner.os }}-build-${{ env.cache-name }} + key: ${{ runner.os }}-${{ matrix.php }}-build-${{ env.cache-name }} - - name: PHP ${{ matrix.php }} ${{ matrix.contao }} Cache vendor directory + - name: Cache vendor directory uses: actions/cache@v3 env: - cache-name: composer-vendor + cache-name: vendor with: path: vendor - key: ${{ runner.os }}-build-${{ env.cache-name }}-${{ hashFiles('**/composer.lock') }} + key: ${{ runner.os }}-${{ matrix.php }}-${{ matrix.contao }}-build-${{ env.cache-name }}-${{ hashFiles('**/composer.lock') }} restore-keys: | - ${{ runner.os }}-build-${{ env.cache-name }}- + ${{ runner.os }}-${{ matrix.php }}-${{ matrix.contao }}-build-${{ env.cache-name }}- - - name: PHP ${{ matrix.php }} ${{ matrix.contao }} Install composer dependencies - run: composer update --prefer-dist --no-interaction --no-suggest + - name: Cache phpcq directory + uses: actions/cache@v3 + 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 - - name: PHP ${{ matrix.php }} ${{ matrix.contao }} Run tests - run: ant -keep-going + # run tests + - name: Run tests + run: ./vendor/bin/phpcq run -v ${{ matrix.output }} + + - name: Upload build directory to artifact + uses: actions/upload-artifact@v3 + if: ${{ success() }} || ${{ failure() }} + with: + name: phpcq-builds-php-${{ matrix.php }}-${{ matrix.contao }} + path: .phpcq/build/ diff --git a/.gitignore b/.gitignore index f0ec851e1..6561ee7c1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ .DS_Store Thumbs.db -# IDEs +# IDEs .buildpath .project .settings/ @@ -16,6 +16,5 @@ vendor/ composer.lock # build -build/ -build.properties -.phpunit.result.cache \ No newline at end of file +.phpunit.result.cache +/.phpcq/* diff --git a/.phpcq.lock b/.phpcq.lock new file mode 100644 index 000000000..d79776ed4 --- /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.19","url":"https://phar.phpunit.de/phpunit-9.6.19.phar","requirements":{"php":{"php":">=7.3","ext-dom":"*","ext-json":"*","ext-libxml":"*","ext-mbstring":"*","ext-xml":"*","ext-xmlwriter":"*"}},"checksum":{"type":"sha-256","value":"f30c21743f4fbea14ad8ab8f47673f9067117262dafa147fcb182e75fbc1c249"},"signature":"https://phar.phpunit.de/phpunit-9.6.19.phar.asc"}},"composerLock":null},"psalm":{"api-version":"1.0.0","version":"1.2.0.0","type":"php-file","url":"https://phpcq.github.io/repository/plugin/psalm/psalm-1.2.0.0.php","signature":null,"requirements":{"php":{"php":"^7.4 || ^8.0","ext-dom":"*"},"tool":{"psalm":"^3.0 || ^4.0 || ^5.0"}},"checksum":{"type":"sha-512","value":"4a550c9226d7bca582d7c10bd87cce01190c96398936b1613421640c83df62ed1c6e0d44c1b39635414ea8cf4a892a6458d27590793238add24e7cb5547e6ffd"},"tools":{"psalm":{"version":"5.24.0","url":"https://github.com/vimeo/psalm/releases/download/5.24.0/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.24.0/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.9.2","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/3.9.2/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.9.2/phpcs.phar.asc"},"phpcbf":{"version":"3.9.2","url":"https://github.com/PHPCSStandards/PHP_CodeSniffer/releases/download/3.9.2/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.9.2/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.42.0","url":"https://github.com/ergebnis/composer-normalize/releases/download/2.42.0/composer-normalize.phar","requirements":{"php":{"php":"~7.4.0 || ~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0","ext-json":"*"}},"checksum":null,"signature":"https://github.com/ergebnis/composer-normalize/releases/download/2.42.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 000000000..6dd1dffd9 --- /dev/null +++ b/.phpcq.yaml.dist @@ -0,0 +1,111 @@ +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 + 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 + # Composer normalize + - C00543248C87FB13 + # phpmd + - A4E55EA12C7C085C + +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/CoreBundle/EventListener/DcGeneral/DefinitionBuilder + - src/CoreBundle/Resources/contao/dca + + 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: + - '*/CoreBundle/Resources/contao/dca/*' + - '*/CoreBundle/Resources/contao/languages/*' + - '*/CoreBundle/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 000000000..78eb716f3 --- /dev/null +++ b/.phpmd.xml @@ -0,0 +1,37 @@ + + + + PHPMD rule set + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/README.md b/README.md index fdda7c717..91e2c185b 100644 --- a/README.md +++ b/README.md @@ -3,14 +3,17 @@ [![Latest Version on Packagist](http://img.shields.io/packagist/v/MetaModels/core.svg)](https://packagist.org/packages/MetaModels/core) [![Installations via composer per month](http://img.shields.io/packagist/dm/MetaModels/core.svg)](https://packagist.org/packages/MetaModels/core) -MetaModels -========== +# MetaModels -So what are MetaModels? ------------------------ +Deutsch - s.u. + +## So what are MetaModels? MetaModels are data models you can configure in the Contao Backend. Every MetaModel consists of various attributes of -certain data types (attribute types are available as extensions and get registered upon installation). +certain data types (attribute types are available as extensions and get registered upon installation). In addition, +several MetaModel can be linked to each other via relations as single or multiple selections can be linked with each +other. This allows very complex data structures to be mapped in Contao, e.g. for product catalogues, employee lists, +event management, etc. Programming knowledge is generally not necessary for the creation not necessary. To present the data on the screen (i.e. website, RSS feed, etc.), you define render settings for the MetaModel which define how the various attribute output shall look like (image sizes, use light boxes, etc.). @@ -21,10 +24,7 @@ can be nested (AND/OR conditions i.e.) and be of various nature. Before you start it is helpful to look at the [MetaModels manual](http://metamodels.readthedocs.org/de/latest/index.html). There you will find actual information about the usage and the installation. -How to use it. --------------- - -### Install +## Install You can install MetaModels core with Contao Manager - search "metamodels/core" - or you can use composer @@ -32,13 +32,16 @@ You can install MetaModels core with Contao Manager - search "metamodels/core" - Then add all the necessary [attributes, filters or MetaModel extensions](https://extensions.contao.org/?q=metamodels). +Please [check the manual to see which versions are available](https://metamodels.readthedocs.io/de/latest/manual/install.html#ubersicht-der-versionen) +and which Contao version works with them. Please also note that the latest version is not always available on Github +and that a newer version may only be available as an "early adopter" with special access - [see Fundrasing](https://now.metamodel.me/en/supporters/fundraising). + For the first evaluation of the possibilities of MetaModels you can also use [metamodels/bundle_start](https://extensions.contao.org/?q=metamodels%252Fbundle_start), which installs some attributes and filters. Please do not forget to perform the migration of the database! -Docs: ------ +## Docs: * [The official MetaModel Documentation (de)](http://metamodels.readthedocs.org/de/latest/index.html) (Currently the main documentation) * [The official MetaModel Documentation (en)](http://metamodels.readthedocs.org/en/latest/index.html) @@ -47,16 +50,14 @@ Docs: Feel free to contribute the MetaModel Documentation in [EN](https://github.com/MetaModels/docs) or [DE](https://github.com/MetaModels/docs-de) -Resources: ----------- +## Resources: * [MetaModels Website](https://now.metamodel.me) * [MetaModels Contao Wiki [DE]](https://de.contaowiki.org/MetaModels) * [MetaModels Contao Community Subforum [DE]](https://community.contao.org/de/forumdisplay.php?149-MetaModels) * [MetaModels Channel on Contao Slack #metamodels](https://contao.slack.com/archives/CKGEBDV60) -History: --------- +## History: Metamodels are the replacement for the famous Catalog extension for [Contao CMS](https://github.com/contao/core). @@ -69,12 +70,51 @@ likely be possible both in implementation and learning curve but that this will resembling only of the name with it's ancestor. Therefore, we rebranded everything as "MetaModels". -Who did it? ------------ +## Who did it? See the [CONTRIBUTORS.md](https://github.com/MetaModels/core/tree/master/CONTRIBUTORS.md) file. -Third Party Licenses: ---------------------- +## Third Party Licenses: Icons: This software uses the [Fugue Icons](http://p.yusukekamiyamane.com) + +## Deutsch + +## Was ist MetaModells? + +MetaModels sind Datenmodelle, die Sie im Contao Backend konfigurieren können. Jedes MetaModel besteht aus verschiedenen +Attributen von bestimmten Datentypen wie Text, Checkbox, Dateien (Attributtypen sind als Erweiterungen verfügbar und +werden bei der Installation registriert). Zudem können mehrere MetaModel über Relationen als Einzel- oder Mehrfachauswahl +miteinander verknüpft werden. Damit lassen sich sehr komplexe Datenstrukturen in Contao abbilden wie z. B. für +Produktkataloge, Mitarbeiterlisten, Eventmanagements usw. Programmierkenntnisse sind in der Regel für die Erstellung +nicht notwendig. + +Um die Daten auf dem Bildschirm (z. B. Website, RSS-Feed, etc.) darzustellen, definieren Sie Rendereinstellungen für +das MetaModel, die festlegen, wie die verschiedenen Attributausgaben aussehen sollen (Bildgrößen, Verwendung von +Lightboxes usw.). + +Das Filtern von Daten in Listenansichten erfordert die Konfiguration von Filtereinstellungen. Filtereinstellungen sind +ein sehr komplexes Thema, da sie verschachtelt (z.B. AND/OR-Bedingungen) und von unterschiedlicher Natur sein können. + +Vor dem Start ist es hilfreich, einen Blick in das [MetaModels Handbuch](http://metamodels.readthedocs.org/de/latest/index.html) +zu werfen. Dort findet man aktuelle Informationen über die Verwendung, Installation und Tipps&Tricks. + +## Installation + +MetaModels Core kann mit dem Contao Manager installiert werden - suchen Sie nach "metamodels/core" - oder Sie können +composer verwenden + +``php public/contao-manager.phar.php composer require metamodels/core`` + +Dann fügen Sie alle notwendigen [Attribute, Filter oder MetaModel-Erweiterungen](https://extensions.contao.org/?q=metamodels) +hinzu, die Sie für ihr Model benötigen. + +Bitte [schauen Sie im Handbuch nach, welche Versionen verfügbar sind](https://metamodels.readthedocs.io/de/latest/manual/install.html#ubersicht-der-versionen) +und welche Contao-Version mit ihnen funktioniert. Bitte beachten Sie auch, dass nicht immer die neueste Version auf +Github verfügbar ist und dass eine neuere Version möglicherweise nur als "Early Adopter" mit speziellem Zugang +erhältlich ist - [siehe Fundrasing](https://now.metamodel.me/en/supporters/fundraising). + +Für eine erste Evaluierung der Möglichkeiten von MetaModels kann man auch [metamodels/bundle_start](https://extensions.contao.org/?q=metamodels%252Fbundle_start) +verwenden, das einige Attribute und Filter installiert. + +Bitte vergessen Sie nicht, die Migration der Datenbank durchzuführen! diff --git a/build.default.properties b/build.default.properties deleted file mode 100644 index 3520e42fc..000000000 --- a/build.default.properties +++ /dev/null @@ -1,24 +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 - -# Exclude known "similar files" from cpd detection - We know these are code smells but can't refactor until 3.0 -# Sadly we can only exclude paths and not files. -phpcpd.excluded=src/CoreBundle/Resources/contao/dca \ - src/Helper \ - src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting \ - src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting \ - src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder \ - -# FIXME: phpcs changed the evaluation of ignored files -# from multiple occurrences of '--ignore' to a comma separated list in 3.0. -phpcs.excluded=*/Resources/public/*,\ -*/Resources/contao/config/*,\ -*/Resources/contao/dca/*,\ -*/Resources/contao/languages/* diff --git a/build.xml b/build.xml deleted file mode 100644 index 61e0fb463..000000000 --- a/build.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - diff --git a/composer.json b/composer.json index 1cedd33f4..09e923ccd 100644 --- a/composer.json +++ b/composer.json @@ -1,19 +1,18 @@ { "name": "metamodels/core", "description": "MetaModels core", + "license": "LGPL-3.0-or-later", + "type": "contao-bundle", "keywords": [ "contao", "metamodels", "core" ], - "type": "contao-bundle", - "homepage": "http://now.metamodel.me/", - "license": "LGPL-3.0-or-later", "authors": [ { "name": "Christian Schiffler", "email": "c.schiffler@cyberspectrum.de", - "homepage": "http://www.cyberspectrum.de", + "homepage": "https://www.cyberspectrum.de", "role": "Developer" }, { @@ -22,6 +21,7 @@ "role": "Developer" } ], + "homepage": "https://now.metamodel.me/", "support": { "email": "mail@metamodel.me", "issues": "https://github.com/MetaModels/core/issues", @@ -30,33 +30,46 @@ "source": "https://github.com/MetaModels/core" }, "require": { - "php": "^7.4", - "contao-community-alliance/dc-general": "^2.2", - "contao-community-alliance/events-contao-bindings": "^4.9", - "contao-community-alliance/meta-palettes": "~2.0", - "contao-community-alliance/translator": "^2.2", - "contao-community-alliance/url-builder": "~1.3", - "contao/core-bundle": "^4.9.0, <4.13.0", - "discordier/justtextwidgets": "^1.2", - "doctrine/cache": "~1.6", - "menatwork/contao-multicolumnwizard-bundle": "^3.4", - "symfony/asset": "^4.4.6", - "symfony/config": "^4.4.6", - "symfony/dependency-injection": "^4.4.6", - "symfony/event-dispatcher": "^4.4.6", - "symfony/filesystem": "^4.4.6", - "symfony/finder": "^4.4.6", + "php": "^8.1", + "ext-dom": "*", + "contao-community-alliance/dc-general": "^2.3.10", + "contao-community-alliance/events-contao-bindings": "^4.13.1", + "contao-community-alliance/meta-palettes": "^2.0.10", + "contao-community-alliance/translator": "^2.4.2", + "contao-community-alliance/url-builder": "^1.3.3", + "contao/core-bundle": "^4.13.5 <5.0", + "discordier/justtextwidgets": "^1.3", + "doctrine/cache": "^2.1", + "doctrine/dbal": "^3.6.0", + "doctrine/orm": "^2.16.0", + "menatwork/contao-multicolumnwizard-bundle": "^3.6.7", + "psr/container": "^1.1.2", + "psr/log": "^2.0.0", + "symfony/asset": "^5.4", + "symfony/cache": "^5.4", + "symfony/config": "^5.4", + "symfony/console": "^5.4", + "symfony/dependency-injection": "^5.4", + "symfony/deprecation-contracts": "^3.3.0", + "symfony/event-dispatcher": "^5.4", + "symfony/event-dispatcher-contracts": "^2.5.2", + "symfony/filesystem": "^5.4", + "symfony/finder": "^5.4", + "symfony/http-foundation": "^5.4", + "symfony/http-kernel": "^5.4", + "symfony/routing": "^5.4", + "symfony/security-core": "^5.4", + "symfony/service-contracts": "^2.5.2", + "symfony/translation": "^5.4", + "symfony/translation-contracts": "^2.5.2", "terminal42/service-annotation-bundle": "^1.0", - "webmozart/path-util": "~2.3" + "twig/twig": "^3.7.0" }, "require-dev": { + "contao/manager-bundle": "^4.13.0 <5.0", "contao/manager-plugin": "^2.8", - "contao/manager-bundle": "^4.9.0, <4.13.0", - "doctrine/doctrine-bundle": "^1.8", - "phpcq/all-tasks": "^1.3" - }, - "conflict": { - "contao/manager-bundle": "4.9.27" + "doctrine/doctrine-bundle": "^1.8.1 || ^2.8.3", + "phpcq/runner-bootstrap": "^1.0@dev" }, "autoload": { "psr-4": { @@ -68,20 +81,24 @@ "MetaModels\\Test\\": "tests/" } }, - "extra": { - "contao-manager-plugin": "MetaModels\\CoreBundle\\ContaoManager\\Plugin", - "contao": { - "runonce": [ - "runonce/runonce.php" - ] - } - }, "config": { "allow-plugins": { "contao-components/installer": false, "contao/manager-plugin": false, - "php-http/discovery": true + "php-http/discovery": false }, "sort-packages": true + }, + "extra": { + "branch-alias": { + "dev-feature/2.3.0": "2.3.x-dev", + "dev-feature/translation_loader": "2.3.x-trans-dev" + }, + "contao": { + "runonce": [ + "runonce/runonce.php" + ] + }, + "contao-manager-plugin": "MetaModels\\CoreBundle\\ContaoManager\\Plugin" } } diff --git a/psalm.xml b/psalm.xml new file mode 100644 index 000000000..6878801dc --- /dev/null +++ b/psalm.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Attribute/AbstractAttributeTypeFactory.php b/src/Attribute/AbstractAttributeTypeFactory.php index 11b57316f..3c13bb402 100644 --- a/src/Attribute/AbstractAttributeTypeFactory.php +++ b/src/Attribute/AbstractAttributeTypeFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author David Molineus - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -34,21 +35,21 @@ abstract class AbstractAttributeTypeFactory implements IAttributeTypeFactory * * @var string */ - protected $typeName; + protected $typeName = ''; /** * The name of the attribute class of this type. * - * @var string + * @var class-string */ - protected $typeClass; + protected $typeClass = IAttribute::class; /** - * The icon representing this attributy type. + * The icon representing this attribute type. * * @var string */ - protected $typeIcon; + protected $typeIcon = ''; /** * {@inheritdoc} @@ -89,7 +90,7 @@ protected function __construct() */ public function isTranslatedType() { - return in_array('MetaModels\Attribute\ITranslated', class_implements($this->typeClass, true)); + return \in_array('MetaModels\Attribute\ITranslated', \class_implements($this->typeClass), true); } /** @@ -99,7 +100,7 @@ public function isTranslatedType() */ public function isSimpleType() { - return in_array('MetaModels\Attribute\ISimple', class_implements($this->typeClass, true)); + return \in_array('MetaModels\Attribute\ISimple', \class_implements($this->typeClass), true); } /** @@ -109,6 +110,6 @@ public function isSimpleType() */ public function isComplexType() { - return in_array('MetaModels\Attribute\IComplex', class_implements($this->typeClass, true)); + return \in_array('MetaModels\Attribute\IComplex', \class_implements($this->typeClass), true); } } diff --git a/src/Attribute/AttributeFactory.php b/src/Attribute/AttributeFactory.php index ad33a7277..d00adbe6f 100644 --- a/src/Attribute/AttributeFactory.php +++ b/src/Attribute/AttributeFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2021 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,15 +33,19 @@ * This is the implementation of the Field factory to query instances of fields. * * Usually this is only used internally by {@link MetaModels\Factory} + * + * @psalm-suppress DeprecatedInterface */ class AttributeFactory implements IAttributeFactory { /** * The service container. * - * @var IMetaModelsServiceContainer + * @var IMetaModelsServiceContainer|null + * + * @psalm-suppress DeprecatedInterface */ - protected $serviceContainer; + protected $serviceContainer = null; /** * The event dispatcher. @@ -54,7 +59,7 @@ class AttributeFactory implements IAttributeFactory * * @var IAttributeTypeFactory[] */ - protected $typeFactories = array(); + protected $typeFactories = []; /** * Create a new instance. @@ -70,11 +75,12 @@ public function __construct(EventDispatcherInterface $eventDispatcher) * Set the service container. * * @param IMetaModelsServiceContainer $serviceContainer The service container to use. - * * @param bool $deprecationNotice Determine deprecated notice. * * @return AttributeFactory * + * @psalm-suppress DeprecatedInterface + * * @deprecated The service container will get removed, use the symfony service container instead. */ public function setServiceContainer(IMetaModelsServiceContainer $serviceContainer, $deprecationNotice = true) @@ -113,6 +119,8 @@ public function setServiceContainer(IMetaModelsServiceContainer $serviceContaine * * @return IMetaModelsServiceContainer * + * @psalm-suppress DeprecatedInterface + * * @deprecated The service container will get removed, use the symfony service container instead. */ public function getServiceContainer() @@ -123,6 +131,10 @@ public function getServiceContainer() E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + if (null === $this->serviceContainer) { + throw new \RuntimeException('The deprecated service container has not been set anymore.'); + } + return $this->serviceContainer; } @@ -130,7 +142,6 @@ public function getServiceContainer() * Create an attribute instance from an information array. * * @param array $information The attribute information. - * * @param IMetaModel $metaModel The MetaModel instance for which the attribute shall be created. * * @return IAttribute|null @@ -143,8 +154,11 @@ public function createAttribute($information, $metaModel) if ($event->getAttribute()) { return $event->getAttribute(); } + if (null === ($type = $information['type'] ?? null)) { + return null; + } - $factory = $this->getTypeFactory($information['type']); + $factory = $this->getTypeFactory($type); if (!$factory) { return null; @@ -175,15 +189,18 @@ public function addTypeFactory(IAttributeTypeFactory $typeFactory) */ public function getTypeFactory($typeFactory) { - return isset($this->typeFactories[(string) $typeFactory]) ? $this->typeFactories[(string) $typeFactory] : null; + return $this->typeFactories[$typeFactory] ?? null; } /** * {@inheritdoc} */ - public function attributeTypeMatchesFlags($name, $flags) + public function attributeTypeMatchesFlags($factory, $flags) { - $factory = $this->getTypeFactory($name); + $factory = $this->getTypeFactory($factory); + if (null === $factory) { + return false; + } // Shortcut, if all are valid, return all. :) if ($flags === self::FLAG_ALL) { @@ -204,8 +221,8 @@ public function getTypeNames($flags = false) $flags = self::FLAG_ALL; } - $result = array(); - foreach (array_keys($this->typeFactories) as $name) { + $result = []; + foreach (\array_keys($this->typeFactories) as $name) { if (!$this->attributeTypeMatchesFlags($name, $flags)) { continue; } @@ -249,6 +266,6 @@ public function createAttributesForMetaModel($metaModel) */ public function getIconForType($type) { - return isset($this->typeFactories[(string) $type]) ? $this->typeFactories[(string) $type]->getTypeIcon() : null; + return isset($this->typeFactories[$type]) ? $this->typeFactories[$type]->getTypeIcon() : ''; } } diff --git a/src/Attribute/Base.php b/src/Attribute/Base.php index 61032c098..681b40da5 100644 --- a/src/Attribute/Base.php +++ b/src/Attribute/Base.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,13 +19,14 @@ * @author David Molineus * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\Attribute; +use MetaModels\Helper\LocaleUtil; use MetaModels\IMetaModel; use MetaModels\ITranslatedMetaModel; use MetaModels\Render\Setting\ISimple as ISimpleRenderSetting; @@ -38,20 +39,25 @@ * This class is the reference implementation for {@link IMetaModelAttribute}. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ abstract class Base implements IAttribute { + use ManagedAttributeTrait; + /** * The MetaModel instance this object belongs to. * * @var IMetaModel */ - private $metaModel; + private IMetaModel $metaModel; /** * The meta information of this attribute. * - * @var array + * @var array */ protected $arrData = []; @@ -61,17 +67,20 @@ abstract class Base implements IAttribute * Note that you should not use this directly but use the factory classes to instantiate attributes. * * @param IMetaModel $objMetaModel The MetaModel instance this attribute belongs to. - * * @param array $arrData The information array, for attribute information, refer to documentation of * table tl_metamodel_attribute and documentation of the certain attribute classes * for information what values are understood. */ - public function __construct(IMetaModel $objMetaModel, $arrData = array()) + public function __construct(IMetaModel $objMetaModel, $arrData = []) { + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ if (!($objMetaModel instanceof ITranslatedMetaModel) && $objMetaModel->isTranslated(false)) { // @codingStandardsIgnoreStart @\trigger_error( - sprintf( + \sprintf( 'Support for translated "\MetaModel\IMetamodel" instances is deprecated since MetaModels 2.2 ' . 'and to be removed in 3.0. The MetaModel "%s" must implement "\MetaModels\ITranslatedMetaModel".', $objMetaModel->getTableName() @@ -82,7 +91,7 @@ public function __construct(IMetaModel $objMetaModel, $arrData = array()) } // Meta information. - foreach (array_intersect($this->getAttributeSettingNames(), array_keys($arrData)) as $strSettingName) { + foreach (\array_intersect($this->getAttributeSettingNames(), \array_keys($arrData)) as $strSettingName) { $this->set($strSettingName, $arrData[$strSettingName]); } $this->metaModel = $objMetaModel; @@ -103,28 +112,32 @@ public function __construct(IMetaModel $objMetaModel, $arrData = array()) */ protected function parameterMask($parameters) { - return implode(',', array_fill(0, count($parameters), '?')); + return \implode(',', \array_fill(0, \count($parameters), '?')); } /** - * Retrieve the human readable name (or title) from the attribute. + * Retrieve the human-readable name (or title) from the attribute. * * If the MetaModel is translated, the currently active language is used, * with properly falling back to the defined fallback language. * - * @return string the human readable name + * @return string the human-readable name */ public function getName() { - if (is_array($this->arrData['name'])) { + if (isset($this->arrData['name']) && \is_array($this->arrData['name'])) { $metaModel = $this->getMetaModel(); - return $this->getLangValue( - $this->get('name'), + $langValue = $this->get('name'); + assert(\is_array($langValue) || \is_string($langValue)); + /** @psalm-suppress DeprecatedMethod */ + return (string) $this->getLangValue( + $langValue, ($metaModel instanceof ITranslatedMetaModel) ? $metaModel->getLanguage() : $metaModel->getActiveLanguage() ) ?: $this->getColName(); } - return $this->arrData['name'] ?: $this->getColName(); + + return $this->arrData['name'] ?? $this->getColName(); } /** @@ -133,51 +146,68 @@ public function getName() * If the language is not contained within the value array, the fallback language from the parenting * {@link IMetaModel} instance is tried as well. * - * @param array $arrValues The array holding all language values in the form array('langcode' => $varValue). + * @param array|string $arrValues The array holding all language values in the form + * array('langcode' => $varValue). + * @param string|null $strLangCode The language code of the language to fetch. * - * @param string $strLangCode The language code of the language to fetch. - * - * @return mixed|null the value for the given language or the fallback language, NULL if neither is present. + * @return string|null the value for the given language or the fallback language, NULL if neither is present. * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - protected function getLangValue($arrValues, $strLangCode = null) + protected function getLangValue($arrValues, $strLangCode = null): ?string { - $metaModel = $this->getMetaModel(); // Not a valid lookup array, exit. - if (!is_array($arrValues)) { + if (!\is_array($arrValues)) { return $arrValues; } + $metaModel = $this->getMetaModel(); + // Not translated, exit. + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if (!($metaModel instanceof ITranslatedMetaModel) && !$metaModel->isTranslated(false)) { + return reset($arrValues); + } + if (null === $strLangCode) { // @codingStandardsIgnoreStart @\trigger_error( - sprintf( + \sprintf( 'Not passing the language code to "%s" is deprecated since MetaModels 2.2 and will fail in 3.0 ', __METHOD__ ), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $strLangCode = \str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); - } - // Not translated, exit. - if (!($metaModel instanceof ITranslatedMetaModel) && !$metaModel->isTranslated(false)) { - return $arrValues; + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + // In future versions we try to read from locale request, otherwise we raise an exception. + /** @psalm-suppress DeprecatedMethod */ + $strLangCode = LocaleUtil::formatAsLocale( + $GLOBALS['TL_LANGUAGE'] + ?? (($this->metaModel instanceof ITranslatedMetaModel) + ? $this->metaModel->getLanguage() + : $this->metaModel->getActiveLanguage()) + ); } - if (array_key_exists($strLangCode, $arrValues)) { + // If empty, use main-language. + if (\array_key_exists($strLangCode, $arrValues) && '' !== $arrValues[$strLangCode]) { return $arrValues[$strLangCode]; } + // Language code not set, use fallback. if ($metaModel instanceof ITranslatedMetaModel) { $strLangCode = $metaModel->getMainLanguage(); } else { - $strLangCode = $metaModel->getFallbackLanguage(); + /** @psalm-suppress DeprecatedMethod */ + $strLangCode = (string) $metaModel->getFallbackLanguage(); } - if (array_key_exists($strLangCode, $arrValues)) { + + if (\array_key_exists($strLangCode, $arrValues)) { return $arrValues[$strLangCode]; } @@ -187,30 +217,36 @@ protected function getLangValue($arrValues, $strLangCode = null) /** * Hook additional attribute formatter that want to format the value. * - * @param array $arrBaseFormatted The current result array. The keys "raw" and "text" are always - * populated. - * - * @param array $arrRowData The Raw values from the database. - * - * @param string $strOutputFormat The output format to use. - * - * @param ISimpleRenderSetting $objSettings The output format settings. + * @param array $arrBaseFormatted The current result array. The keys "raw" and "text" are always + * populated. + * @param array $arrRowData The Raw values from the database. + * @param string $strOutputFormat The output format to use. + * @param ISimpleRenderSetting|null $objSettings The output format settings. * * @return mixed * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) + * + * @deprecated This will get removed in 3.0. */ public function hookAdditionalFormatters($arrBaseFormatted, $arrRowData, $strOutputFormat, $objSettings) { $arrResult = $arrBaseFormatted; - if (isset($GLOBALS['METAMODEL_HOOKS']['parseValue']) && is_array($GLOBALS['METAMODEL_HOOKS']['parseValue'])) { + if (isset($GLOBALS['METAMODEL_HOOKS']['parseValue']) && \is_array($GLOBALS['METAMODEL_HOOKS']['parseValue'])) { + // @codingStandardsIgnoreStart + @trigger_error( + '"' .__METHOD__ . '" is deprecated and will get removed.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + foreach ($GLOBALS['METAMODEL_HOOKS']['parseValue'] as $callback) { - list($strClass, $strMethod) = $callback; + [$strClass, $strMethod] = $callback; - $objCallback = (in_array('getInstance', get_class_methods($strClass))) - ? call_user_func(array($strClass, 'getInstance')) + $objCallback = (\in_array('getInstance', \get_class_methods($strClass))) + ? \call_user_func(array($strClass, 'getInstance')) : new $strClass(); $arrResult = $objCallback->$strMethod( @@ -230,24 +266,22 @@ public function hookAdditionalFormatters($arrBaseFormatted, $arrRowData, $strOut * When rendered via a template, this populates the template with values. * * @param Template $objTemplate The Template instance to populate. - * * @param array $arrRowData The row data for the current item. - * * @param ISimpleRenderSetting $objSettings The render settings to use for this attribute. * * @return void */ protected function prepareTemplate(Template $objTemplate, $arrRowData, $objSettings) { + $additionalClass = (string) $objSettings->get('additional_class'); + $objTemplate->setData( [ 'attribute' => $this, 'settings' => $objSettings, 'row' => $arrRowData, - 'raw' => $arrRowData[$this->getColName()], - 'additional_class' => $objSettings->get('additional_class') - ? ' ' . $objSettings->get('additional_class') - : '' + 'raw' => ($arrRowData[$this->getColName()] ?? null), + 'additional_class' => $additionalClass ? ' ' . $additionalClass : '' ] ); } @@ -257,7 +291,10 @@ protected function prepareTemplate(Template $objTemplate, $arrRowData, $objSetti */ public function getColName() { - return $this->arrData['colname']; + $colName = $this->arrData['colname']; + \assert(\is_string($colName)); + + return $colName; } /** @@ -273,7 +310,7 @@ public function getMetaModel() */ public function get($strKey) { - return isset($this->arrData[$strKey]) ? $this->arrData[$strKey] : null; + return $this->arrData[$strKey] ?? null; } /** @@ -282,8 +319,8 @@ public function get($strKey) public function set($strKey, $varValue) { if (in_array($strKey, $this->getAttributeSettingNames())) { - if (!is_array($varValue) && (substr($varValue, 0, 2) == 'a:')) { - $unSerialized = unserialize($varValue); + if (null !== $varValue && !\is_array($varValue) && (\str_starts_with($varValue, 'a:'))) { + $unSerialized = \unserialize($varValue); } if (isset($unSerialized) && is_array($unSerialized)) { @@ -298,28 +335,52 @@ public function set($strKey, $varValue) /** * {@inheritdoc} + * + * @deprecated Implement schema generators instead - see #1267. */ public function handleMetaChange($strMetaName, $varNewValue) { - // By default we accept any change of meta information. + // By default, we accept any change of meta information. $this->set($strMetaName, $varNewValue); + if ($this->isManagedAttribute($this->get('type'))) { + $this->triggerDeprecationShouldNotCallManaged(static::class, __METHOD__); + return $this; + } + $this->triggerDeprecationIsUnmanagedAttribute(static::class, __METHOD__); + return $this; } /** * {@inheritdoc} + * + * @deprecated Implement schema generators instead - see #1267. */ public function destroyAUX() { + if ($this->isManagedAttribute($this->get('type'))) { + $this->triggerDeprecationShouldNotCallManaged(static::class, __METHOD__); + return; + } + + $this->triggerDeprecationIsUnmanagedAttribute(static::class, __METHOD__); // No-op. } /** * {@inheritdoc} + * + * @deprecated Implement schema generators instead - see #1267. */ public function initializeAUX() { + if ($this->isManagedAttribute($this->get('type'))) { + $this->triggerDeprecationShouldNotCallManaged(static::class, __METHOD__); + return; + } + + $this->triggerDeprecationIsUnmanagedAttribute(static::class, __METHOD__); // No-op. } @@ -340,7 +401,9 @@ public function getAttributeSettingNames() 'isvariant', // Settings originating from tl_metamodel_dcasetting. 'tl_class', - 'readonly' + 'readonly', + 'be_template', + 'fe_template', ]; } @@ -352,15 +415,21 @@ public function getAttributeSettingNames() * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - private function setLanguageStrings() + private function setLanguageStrings(): void { // Only overwrite the language if not already set. if (empty($GLOBALS['TL_LANG'][$this->getMetaModel()->getTableName()][$this->getColName()])) { - $GLOBALS['TL_LANG'][$this->getMetaModel()->getTableName()][$this->getColName()] = array - ( - $this->getLangValue($this->get('name'), \str_replace('-', '_', $GLOBALS['TL_LANGUAGE'])), - $this->getLangValue($this->get('description'), \str_replace('-', '_', $GLOBALS['TL_LANGUAGE'])), - ); + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + /** @psalm-suppress DeprecatedMethod */ + $language = LocaleUtil::formatAsLocale($GLOBALS['TL_LANGUAGE'] ?? ( + ($this->metaModel instanceof ITranslatedMetaModel) + ? $this->metaModel->getLanguage() + : $this->metaModel->getActiveLanguage())); + + $GLOBALS['TL_LANG'][$this->getMetaModel()->getTableName()][$this->getColName()] = [ + $this->getLangValue($this->get('name'), $language), + $this->getLangValue($this->get('description'), $language), + ]; } } @@ -386,6 +455,7 @@ private function getBaseDefinition() } if (!isset($definition['label'])) { + /** @psalm-suppress UnsupportedReferenceUsage */ $definition['label'] = &$GLOBALS['TL_LANG'][$tableName][$this->getColName()]; } @@ -411,7 +481,6 @@ private function isAllowedValue($name) * Extract an override value. * * @param string $name The name of the value. - * * @param array $overrides The overrides containing the values to be overridden. * * @return mixed @@ -429,7 +498,6 @@ protected function getOverrideValue($name, $overrides) * Extract an override value. * * @param array $fieldDefinition The field definition. - * * @param array $overrides The overrides containing the values to be overridden. * * @return array @@ -454,11 +522,14 @@ private function setBaseEval($fieldDefinition, $overrides) 'spaceToUnderscore', 'includeBlankOption', 'submitOnChange', - 'readonly' + 'readonly', + 'be_template', + 'fe_template', ]; foreach ($names as $name) { - if (empty($fieldDefinition['eval'][$name]) + if ( + empty($fieldDefinition['eval'][$name]) && ($value = $this->getOverrideValue($name, $overrides)) ) { $fieldDefinition['eval'][$name] = $value; @@ -476,14 +547,14 @@ private function setBaseEval($fieldDefinition, $overrides) /** * {@inheritdoc} */ - public function getFieldDefinition($arrOverrides = array()) + public function getFieldDefinition($arrOverrides = []) { $arrFieldDef = $this->setBaseEval($this->getBaseDefinition(), $arrOverrides); if ($this->isAllowedValue('trailingSlash')) { - $trailingSlash = $this->getOverrideValue('trailingSlash', $arrOverrides); - if ($trailingSlash != 2) { - $arrFieldDef['eval']['trailingSlash'] = (bool) $arrOverrides['trailingSlash']; + $trailingSlash = (int) $this->getOverrideValue('trailingSlash', $arrOverrides); + if ($trailingSlash !== 2) { + $arrFieldDef['eval']['trailingSlash'] = (bool) ($arrOverrides['trailingSlash'] ?? false); } } @@ -508,14 +579,12 @@ public function getFieldDefinition($arrOverrides = array()) */ public function getItemDCA($arrOverrides = []) { - $arrReturn = [ - 'fields' => array_merge( + return [ + 'fields' => \array_merge( [$this->getColName() => $this->getFieldDefinition($arrOverrides)], - (array) $GLOBALS['TL_DCA'][$this->getMetaModel()->getTableName()]['fields'][$this->getColName()] + (array) ($GLOBALS['TL_DCA'][$this->getMetaModel()->getTableName()]['fields'][$this->getColName()] ?? []) ), ]; - - return $arrReturn; } /** @@ -539,9 +608,7 @@ public function widgetToValue($varValue, $itemId) */ public function getDefaultRenderSettings() { - $objSetting = new Simple(['template' => 'mm_attr_' . $this->get('type')]); - - return $objSetting; + return new Simple(['template' => 'mm_attr_' . ($this->get('type') ?? '')]); } /** @@ -549,12 +616,9 @@ public function getDefaultRenderSettings() */ public function parseValue($arrRowData, $strOutputFormat = 'text', $objSettings = null) { - $arrResult = ['raw' => $arrRowData[$this->getColName()]]; - - /** @var ISimpleRenderSetting $objSettings */ - if ($objSettings && $objSettings->get('template')) { - $strTemplate = $objSettings->get('template'); + $arrResult = ['raw' => ($arrRowData[$this->getColName()] ?? null)]; + if ($objSettings && ($strTemplate = (string) $objSettings->get('template'))) { $objTemplate = new Template($strTemplate); $this->prepareTemplate($objTemplate, $arrRowData, $objSettings); @@ -566,13 +630,13 @@ public function parseValue($arrRowData, $strOutputFormat = 'text', $objSettings // Text rendering is mandatory, try with the current setting, // upon exception, try again with the default settings, as the template name might have changed. - // if this fails again, we are definately out of luck and bail the exception. + // if this fails again, we are definitely out of luck and bail the exception. try { $arrResult['text'] = $objTemplate->parse('text', true); } catch (\Exception $e) { $objSettingsFallback = $this->getDefaultRenderSettings()->setParent($objSettings->getParent()); - $objTemplate = new Template($objSettingsFallback->get('template')); + $objTemplate = new Template($objSettingsFallback->get('template') ?? ''); $this->prepareTemplate($objTemplate, $arrRowData, $objSettingsFallback); $arrResult['text'] = $objTemplate->parse('text', true); @@ -583,6 +647,7 @@ public function parseValue($arrRowData, $strOutputFormat = 'text', $objSettings } // HOOK: apply additional formatters to attribute. + /** @psalm-suppress DeprecatedMethod */ $arrResult = $this->hookAdditionalFormatters($arrResult, $arrRowData, $strOutputFormat, $objSettings); return $arrResult; @@ -598,7 +663,7 @@ public function getFilterUrlValue($varValue) // We are parsing as text here as this was the way before this method was implemented. See #216. $arrResult = $this->parseValue([$this->getColName() => $varValue], 'text'); - return $arrResult['text']; + return (string) $arrResult['text']; } /** @@ -649,7 +714,7 @@ public function filterLessThan($varValue, $blnInclusive = false) */ public function filterNotEqual($varValue) { - return array_merge($this->filterLessThan($varValue), $this->filterGreaterThan($varValue)); + return \array_merge($this->filterLessThan($varValue) ?? [], $this->filterGreaterThan($varValue) ?? []); } /** diff --git a/src/Attribute/BaseSimple.php b/src/Attribute/BaseSimple.php index b16d135c3..9d3666c92 100644 --- a/src/Attribute/BaseSimple.php +++ b/src/Attribute/BaseSimple.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,7 +18,7 @@ * @author Sven Baumann * @author David Molineus * @author Marc Reimann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,10 +26,17 @@ namespace MetaModels\Attribute; use Contao\System; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\Helper\TableManipulator; use MetaModels\IMetaModel; +use function array_map; +use function is_array; +use function serialize; +use function str_replace; + /** * Reference implementation for Simple attributes. * Simple fields are fields that only consist of one column in the metamodel table and therefore do not need @@ -39,6 +46,8 @@ */ class BaseSimple extends Base implements ISimple { + use ManagedAttributeTrait; + /** * Database connection. * @@ -58,13 +67,13 @@ class BaseSimple extends Base implements ISimple * * Note that you should not use this directly but use the factory classes to instantiate attributes. * - * @param IMetaModel $objMetaModel The MetaModel instance this attribute belongs to. - * @param array $arrData The information array, for attribute information, refer to - * documentation of table tl_metamodel_attribute and documentation of the - * certain attribute classes for information what values are understood. - * @param Connection $connection The database connection. - * - * @param TableManipulator $tableManipulator Table manipulator instance. + * @param IMetaModel $objMetaModel The MetaModel instance this attribute belongs to. + * @param array $arrData The information array, for attribute information, refer to + * documentation of table tl_metamodel_attribute and documentation + * of the certain attribute classes for information what values are + * understood. + * @param Connection|null $connection The database connection. + * @param TableManipulator|null $tableManipulator Table manipulator instance. */ public function __construct( IMetaModel $objMetaModel, @@ -82,7 +91,9 @@ public function __construct( ); // @codingStandardsIgnoreEnd $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); } + $this->connection = $connection; if (null === $tableManipulator) { // @codingStandardsIgnoreStart @@ -93,9 +104,8 @@ public function __construct( // @codingStandardsIgnoreEnd $tableManipulator = System::getContainer()->get('metamodels.table_manipulator'); + assert($tableManipulator instanceof TableManipulator); } - - $this->connection = $connection; $this->tableManipulator = $tableManipulator; } @@ -104,26 +114,38 @@ public function __construct( * * This tells the attribute to perform any actions that must be done to correctly initialize the new value * and to perform any action to undo the changes that had been done for the previous value. - * i.e.: when an attribute type needs columns in an an auxiliary table, these will have to be updated herein. + * i.e.: when an attribute type needs columns in an auxiliary table, these will have to be updated herein. * * This method may throw an exception, when the new value is invalid or any problems appear, the MetaModelAttribute * will then keep the old meta value. * * @param string $strMetaName Name of the meta information that shall be updated. + * @param mixed $varNewValue The new value for this meta information. * - * @param mixed $varNewValue The new value for this meta information. + * @return IAttribute The instance of this attribute, to support chaining. * - * @return \MetaModels\Attribute\IAttribute The instance of this attribute, to support chaining. + * @throws Exception + * + * @deprecated Implement schema generators instead. */ public function handleMetaChange($strMetaName, $varNewValue) { - // By default we accept any change of meta information. - if ($strMetaName == 'colname') { - if ($this->get($strMetaName) != $varNewValue) { + if ($this->isManagedAttribute($this->get('type'))) { + $this->triggerDeprecationShouldNotCallManaged(static::class, __METHOD__); + return $this; + } + + // By default, we accept any change of meta information. + if ($strMetaName === 'colname') { + if ($this->get($strMetaName) !== $varNewValue) { + /** @psalm-suppress DeprecatedMethod */ $this->renameColumn($varNewValue); } + return $this; } + + /** @psalm-suppress DeprecatedMethod */ return parent::handleMetaChange($strMetaName, $varNewValue); } @@ -133,6 +155,8 @@ public function handleMetaChange($strMetaName, $varNewValue) * @param mixed $arrValues The values to be stored into database. Mapping is item id=>value. * * @return void + * + * @throws Exception */ public function setDataFor($arrValues) { @@ -146,12 +170,14 @@ public function setDataFor($arrValues) ->set('t.' . $strColName, ':' . $strColName) ->setParameter($strColName, is_array($varData) ? serialize($varData) : $varData) ->setParameter('id', $intId) - ->execute(); + ->executeQuery(); } } /** * {@inheritDoc} + * + * @throws Exception */ public function getFilterOptions($idList, $usedOnly, &$arrCount = null) { @@ -168,25 +194,26 @@ public function getFilterOptions($idList, $usedOnly, &$arrCount = null) ->where('t.id IN (:ids)') ->groupBy('t.' . $strCol) ->orderBy('MIN(FIELD(t.id, :ids))') - ->setParameter('ids', $idList, Connection::PARAM_STR_ARRAY) - ->execute(); + ->setParameter('ids', $idList, ArrayParameterType::STRING) + ->executeQuery(); } else { $statement = $this->connection->createQueryBuilder() ->select('t.' . $strCol . ', COUNT(t.' . $strCol . ') as mm_count') ->from($this->getMetaModel()->getTableName(), 't') ->groupBy('t.' . $strCol) ->orderBy('t.' . $strCol) - ->execute(); + ->executeQuery(); } $arrResult = []; - while ($objRow = $statement->fetch(\PDO::FETCH_OBJ)) { + while ($objRow = $statement->fetchAssociative()) { if (is_array($arrCount)) { - $arrCount[$objRow->$strCol] = $objRow->mm_count; + $arrCount[$objRow[$strCol]] = $objRow['mm_count']; } - $arrResult[$objRow->$strCol] = $objRow->$strCol; + $arrResult[$objRow[$strCol]] = $objRow[$strCol]; } + return $arrResult; } @@ -194,20 +221,23 @@ public function getFilterOptions($idList, $usedOnly, &$arrCount = null) * {@inheritdoc} * * This base implementation does a plain SQL sort by native value as defined by MySQL. + * + * @throws Exception */ public function sortIds($idList, $strDirection) { // Base implementation, do a simple sorting on given column. - $idList = $this->connection->createQueryBuilder() + $statement = $this->connection + ->createQueryBuilder() ->select('t.id') ->from($this->getMetaModel()->getTableName(), 't') ->where('t.id IN (:ids)') - ->setParameter('ids', $idList, Connection::PARAM_STR_ARRAY) + ->setParameter('ids', $idList, ArrayParameterType::STRING) ->orderBy('t.' . $this->getColName(), $strDirection) - ->execute() - ->fetchAll(\PDO::FETCH_COLUMN, 'id'); + ->executeQuery(); - return $idList; + // 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()); } /** @@ -218,19 +248,25 @@ public function sortIds($idList, $strDirection) * * @param string $strPattern The text to search for. This may contain wildcards. * - * @return int[] the ids of matching items. + * @return list The ids of matching items. + * + * @throws Exception */ public function searchFor($strPattern) { // Base implementation, do a simple search on given column. - $strPattern = str_replace(array('*', '?'), array('%', '_'), $strPattern); - return $this->connection->createQueryBuilder() + $strPattern = str_replace(['*', '?'], ['%', '_'], $strPattern); + + $statement = $this->connection + ->createQueryBuilder() ->select('t.id') ->from($this->getMetaModel()->getTableName(), 't') ->where('t.' . $this->getColName() . ' LIKE :pattern') ->setParameter('pattern', $strPattern) - ->execute() - ->fetchAll(\PDO::FETCH_COLUMN, 'id'); + ->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()); } /** @@ -240,9 +276,15 @@ public function searchFor($strPattern) * Deriving classes SHOULD override this function. * * @return string 'blob NULL' + * + * @deprecated Implement schema generators instead - see #1267. */ public function getSQLDataType() { + if ($this->isManagedAttribute($this->get('type'))) { + $this->triggerDeprecationShouldNotCallManaged(static::class, __METHOD__); + } + return 'blob NULL'; } @@ -250,10 +292,23 @@ public function getSQLDataType() * Create auxiliary data like a column in the MetaModel table or references in another table etc. * * @return void + * + * @deprecated Implement schema generators instead - see #1267. */ 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(); + + /** @psalm-suppress DeprecatedMethod */ $this->deleteColumn(); } @@ -261,10 +316,25 @@ public function destroyAUX() * Delete all auxiliary data like a column in the MetaModel table or references in another table etc. * * @return void + * + * @throws Exception + * + * @deprecated Implement schema generators instead - see #1267. */ 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(); + + /** @psalm-suppress DeprecatedMethod */ $this->createColumn(); } @@ -274,10 +344,23 @@ public function initializeAUX() * You have to override this function in field types, when you want to have multi column structure etc. * * @return void + * + * @throws Exception + * + * @deprecated Implement schema generators instead - see #1267. */ public function createColumn() { + if ($this->isManagedAttribute($this->get('type'))) { + $this->triggerDeprecationShouldNotCallManaged(static::class, __METHOD__); + + return; + } + + $this->triggerDeprecationIsUnmanagedAttribute(static::class, __METHOD__); + if ($this->getColName()) { + /** @psalm-suppress DeprecatedMethod */ $this->tableManipulator->createColumn( $this->getMetaModel()->getTableName(), $this->getColName(), @@ -290,10 +373,22 @@ public function createColumn() * Removes the underlying database structure for this field. * * @return void + * + * @throws Exception + * + * @deprecated Implement schema generators instead - see #1267. */ public function deleteColumn() { - $schemaManager = $this->connection->getSchemaManager(); + if ($this->isManagedAttribute($this->get('type'))) { + $this->triggerDeprecationShouldNotCallManaged(static::class, __METHOD__); + + return; + } + + $this->triggerDeprecationIsUnmanagedAttribute(static::class, __METHOD__); + + $schemaManager = $this->connection->createSchemaManager(); $columns = $schemaManager->listTableColumns($this->getMetaModel()->getTableName()); // Try to delete the column. If it does not exist as we can assume it has been deleted already then. @@ -308,15 +403,28 @@ public function deleteColumn() * @param string $strNewColumnName The new column name. * * @return void + * + * @throws Exception + * + * @deprecated Implement schema generators instead - see #1267. */ public function renameColumn($strNewColumnName) { + if ($this->isManagedAttribute($this->get('type'))) { + $this->triggerDeprecationShouldNotCallManaged(static::class, __METHOD__); + + return; + } + + $this->triggerDeprecationIsUnmanagedAttribute(static::class, __METHOD__); + $this->tableManipulator->checkColumnName($strNewColumnName); - $schemaManager = $this->connection->getSchemaManager(); + $schemaManager = $this->connection->createSchemaManager(); $columns = $schemaManager->listTableIndexes($this->getMetaModel()->getTableName()); if ($this->getColName() && isset($columns[$this->getColName()])) { + /** @psalm-suppress DeprecatedMethod */ $this->tableManipulator->renameColumn( $this->getMetaModel()->getTableName(), $this->getColName(), @@ -326,6 +434,7 @@ public function renameColumn($strNewColumnName) } else { $strBackupColName = $this->getColName(); $this->set('colname', $strNewColumnName); + /** @psalm-suppress DeprecatedMethod */ $this->createColumn(); $this->set('colname', $strBackupColName); } @@ -348,10 +457,14 @@ public function unserializeData($value) * * @param mixed $value The input value. * - * @return string + * @return string|null */ public function serializeData($value) { - return $value; + if (empty($value)) { + return null; + } + + return (string) $value; } } diff --git a/src/Attribute/Events/CollectMetaModelAttributeInformationEvent.php b/src/Attribute/Events/CollectMetaModelAttributeInformationEvent.php index f43f56de6..ca3eab987 100644 --- a/src/Attribute/Events/CollectMetaModelAttributeInformationEvent.php +++ b/src/Attribute/Events/CollectMetaModelAttributeInformationEvent.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2022 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2022 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,7 +22,7 @@ namespace MetaModels\Attribute\Events; use MetaModels\IMetaModel; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered for every metamodel when the attribute information for the MetaModel shall be retrieved. @@ -33,7 +34,7 @@ class CollectMetaModelAttributeInformationEvent extends Event /** * The event name. */ - const NAME = 'metamodels.metamodel.collect-metamodel-attribute-information'; + public const NAME = 'metamodels.metamodel.collect-metamodel-attribute-information'; /** * The MetaModel instance being created. diff --git a/src/Attribute/Events/CreateAttributeEvent.php b/src/Attribute/Events/CreateAttributeEvent.php index cbaf752f7..6ecd2932d 100644 --- a/src/Attribute/Events/CreateAttributeEvent.php +++ b/src/Attribute/Events/CreateAttributeEvent.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,7 @@ use MetaModels\Attribute\IAttribute; use MetaModels\IMetaModel; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered for every attribute when the factory wants to create an instance. @@ -32,7 +33,7 @@ class CreateAttributeEvent extends Event /** * The event name. */ - const NAME = 'metamodels.attribute.create'; + public const NAME = 'metamodels.attribute.create'; /** * The attribute information. @@ -51,7 +52,7 @@ class CreateAttributeEvent extends Event /** * The attribute instance. * - * @var IAttribute + * @var IAttribute|null */ protected $attribute; @@ -59,7 +60,6 @@ class CreateAttributeEvent extends Event * Create a new instance. * * @param array $attributeInformation The attribute information array. - * * @param IMetaModel $metaModel The MetaModel instance for which the attribute shall get created for. */ public function __construct($attributeInformation, $metaModel) @@ -91,7 +91,7 @@ public function getMetaModel() /** * Retrieve the attribute instance. * - * @return IAttribute + * @return IAttribute|null */ public function getAttribute() { diff --git a/src/Attribute/Events/CreateAttributeFactoryEvent.php b/src/Attribute/Events/CreateAttributeFactoryEvent.php index 12fec04bf..22146d7f8 100644 --- a/src/Attribute/Events/CreateAttributeFactoryEvent.php +++ b/src/Attribute/Events/CreateAttributeFactoryEvent.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2022 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2022 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,7 +22,7 @@ namespace MetaModels\Attribute\Events; use MetaModels\Attribute\IAttributeFactory; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered for every attribute factory instance that is created. diff --git a/src/Attribute/IAttribute.php b/src/Attribute/IAttribute.php index 0ef30766d..775362930 100644 --- a/src/Attribute/IAttribute.php +++ b/src/Attribute/IAttribute.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author David Maack * @author Stefan Heimes * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,17 +36,17 @@ interface IAttribute { /** - * Retrieve the human readable name (or title) from the attribute. + * Retrieve the human-readable name (or title) from the attribute. * * If the MetaModel is translated, the currently active language is used, * with properly falling back to the defined fallback language. * - * @return string the human readable name + * @return string the human-readable name */ public function getName(); /** - * Queries the attribute for it's column name within it's MetaModel. + * Queries the attribute for its column name within it's MetaModel. * * @return string the attributes column name. */ @@ -70,13 +71,12 @@ public function get($strKey); /** * Override a meta information setting. * - * All changes to an attribute via set() are considered to be non persistent and therefore will not update any + * All changes to an attribute via set() are considered to be non-persistent and therefore will not update any * structural information or auxiliary properties that might be needed within the attribute type. * * For persistent updates, use {@link IAttribute::handleMetaChange()} instead. * * @param string $strKey The meta information name that shall be set. - * * @param mixed $varValue The value to set. * * @return IAttribute Instance of this attribute, for chaining support. @@ -94,10 +94,11 @@ public function set($strKey, $varValue); * will then keep the old meta value. * * @param string $strMetaName Name of the meta information that shall be updated. - * * @param mixed $varNewValue The new value for this meta information. * * @return IAttribute The instance of this attribute, to support chaining. + * + * @deprecated Implement schema manipulators instead - see #1267. */ public function handleMetaChange($strMetaName, $varNewValue); @@ -105,6 +106,8 @@ public function handleMetaChange($strMetaName, $varNewValue); * Delete all auxiliary data like a column in the MetaModel table or references in another table etc. * * @return void + * + * @deprecated Implement schema generators instead - see #1267. */ public function destroyAUX(); @@ -112,14 +115,16 @@ public function destroyAUX(); * Create auxiliary data like a column in the MetaModel table or references in another table etc. * * @return void + * + * @deprecated Implement schema generators instead - see #1267. */ public function initializeAUX(); /** * Returns all valid settings for the attribute type. * - * @return string[] All valid setting names, this re-ensembles the columns in tl_metamodel_attribute - * this attribute class understands. + * @return list All valid setting names, this re-ensembles the columns in tl_metamodel_attribute + * this attribute class understands. */ public function getAttributeSettingNames(); @@ -130,8 +135,8 @@ public function getAttributeSettingNames(); * Using the optional override parameter, settings known by this attribute can be overridden for the * generating of the output array. * - * @param array $arrOverrides The values to override, for a list of valid parameters, call - * getAttributeSettingNames(). + * @param array $arrOverrides The values to override, for a list of valid parameters, call + * getAttributeSettingNames(). * * @return array The DCA array to use as $GLOBALS['TL_DCA']['tablename']['fields']['attribute-name] */ @@ -145,7 +150,7 @@ public function getFieldDefinition($arrOverrides = array()); * Due to the fact that it calls getFieldDefinition() internally, the result at least contains * the sub array 'fields' with the information of this field's settings. * - * @param array $arrOverrides See documentation in getFieldDefinition() method. + * @param array $arrOverrides See documentation in getFieldDefinition() method. * * @return array The DCA array to use as $GLOBALS['tablename'] * @@ -175,7 +180,6 @@ public function valueToWidget($varValue); * value. * * @param mixed $varValue The value to be transformed. - * * @param string $itemId The id of the item the value belongs to. * * @return mixed The resulting native value @@ -185,7 +189,7 @@ public function widgetToValue($varValue, $itemId); /** * 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. + * @param array $arrValues The values to be stored into database. Mapping is item id=>value. * * @return void */ @@ -206,9 +210,7 @@ public function getDefaultRenderSettings(); * Each attribute class MAY return as many other values in this array with custom keys as it wants. * * @param array $arrRowData The (native) row data from the MetaModel table. - * * @param string $strOutputFormat The desired output format. - * * @param ISimpleRenderSetting|null $objSettings Custom settings to be passed to the renderer. * * @return array An array with all the converted data. @@ -227,11 +229,10 @@ public function getFilterUrlValue($varValue); /** * Sorts the given array list by field value in the given direction. * - * @param string[] $idList A list of Ids from the MetaModel table. - * - * @param string $strDirection The direction for sorting. either 'ASC' or 'DESC', as in plain SQL. + * @param list $idList A list of Ids from the MetaModel table. + * @param string $strDirection The direction for sorting. either 'ASC' or 'DESC', as in plain SQL. * - * @return string[] The sorted array. + * @return list The sorted array. */ public function sortIds($idList, $strDirection); @@ -247,12 +248,10 @@ public function sortIds($idList, $strDirection); * This is only relevant, when using "null" as id list for attributes that have pre configured * values like select lists and tags i.e. * - * @param string[]|null $idList The ids of items that the values shall be fetched from - * (If empty or null, all items). - * - * @param bool $usedOnly Determines if only "used" values shall be returned. - * - * @param array|null $arrCount Array for the counted values. + * @param list|null $idList The ids of items that the values shall be fetched from + * (If empty or null, all items). + * @param bool $usedOnly Determines if only "used" values shall be returned. + * @param array|null $arrCount Array for the counted values. * * @return array All options matching the given conditions as name => value. */ @@ -265,7 +264,7 @@ public function getFilterOptions($idList, $usedOnly, &$arrCount = null); * * @param string $strPattern The text to search for. This may contain wildcards. * - * @return string[]|null The list of item ids of all items matching the condition or null if all match. + * @return list The list of item ids of all items matching the condition or null if all match. */ public function searchFor($strPattern); @@ -273,10 +272,9 @@ public function searchFor($strPattern); * Filter all values greater than the passed value. * * @param mixed $varValue The value to use as lower end. - * * @param bool $blnInclusive If true, the passed value will be included, if false, it will be excluded. * - * @return string[]|null The list of item ids of all items matching the condition or null if all match. + * @return list|null The list of item ids of all items matching the condition or null if all match. */ public function filterGreaterThan($varValue, $blnInclusive = false); @@ -284,10 +282,9 @@ public function filterGreaterThan($varValue, $blnInclusive = false); * Filter all values less than the passed value. * * @param mixed $varValue The value to use as upper end. - * * @param bool $blnInclusive If true, the passed value will be included, if false, it will be excluded. * - * @return string[]|null The list of item ids of all items matching the condition or null if all match. + * @return list|null The list of item ids of all items matching the condition or null if all match. */ public function filterLessThan($varValue, $blnInclusive = false); @@ -296,7 +293,7 @@ public function filterLessThan($varValue, $blnInclusive = false); * * @param mixed $varValue The value to use as upper end. * - * @return string[]|null The list of item ids of all items matching the condition or null if all match. + * @return list|null The list of item ids of all items matching the condition or null if all match. */ public function filterNotEqual($varValue); diff --git a/src/Attribute/IAttributeFactory.php b/src/Attribute/IAttributeFactory.php index 8118c62fa..8b272abe7 100644 --- a/src/Attribute/IAttributeFactory.php +++ b/src/Attribute/IAttributeFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,36 +33,35 @@ interface IAttributeFactory extends IServiceContainerAware /** * Flag for filtering translated attributes. */ - const FLAG_INCLUDE_TRANSLATED = 1; + public const FLAG_INCLUDE_TRANSLATED = 1; /** * Flag for translated attributes. */ - const FLAG_INCLUDE_SIMPLE = 2; + public const FLAG_INCLUDE_SIMPLE = 2; /** * Flag for complex attributes. */ - const FLAG_INCLUDE_COMPLEX = 4; + public const FLAG_INCLUDE_COMPLEX = 4; /** * Flag for retrieving all attribute types. */ - const FLAG_ALL = 7; + public const FLAG_ALL = 7; /** * Flag for filtering untranslated attributes. * * NOTE: When using this flag, translated complex and translated simple types will also get returned. */ - const FLAG_ALL_UNTRANSLATED = 6; + public const FLAG_ALL_UNTRANSLATED = 6; /** * Create an attribute instance from an information array. * - * @param array $information The attribute information. - * - * @param IMetaModel $metaModel The MetaModel instance for which the attribute shall be created. + * @param array $information The attribute information. + * @param IMetaModel $metaModel The MetaModel instance for which the attribute shall be created. * * @return IAttribute|null */ @@ -81,7 +81,7 @@ public function addTypeFactory(IAttributeTypeFactory $typeFactory); * * @param string $typeFactory The name of the type factory to retrieve. * - * @return IAttributeTypeFactory + * @return IAttributeTypeFactory|null */ public function getTypeFactory($typeFactory); @@ -99,9 +99,9 @@ public function attributeTypeMatchesFlags($factory, $flags); /** * Retrieve the type names registered in the factory. * - * @param bool|int $flags The flags for retrieval. See the interface constants for the different values. + * @param false|int $flags The flags for retrieval. See the interface constants for the different values. * - * @return string[] + * @return list */ public function getTypeNames($flags = false); diff --git a/src/Attribute/IComplex.php b/src/Attribute/IComplex.php index 1d173dc2b..2b5603d49 100644 --- a/src/Attribute/IComplex.php +++ b/src/Attribute/IComplex.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Maack * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,17 +32,17 @@ interface IComplex extends IAttribute /** * This method is called to retrieve the data for certain items from the database. * - * @param string[] $arrIds The ids of the items to retrieve. + * @param list $arrIds The ids of the items to retrieve. * - * @return mixed[] The nature of the resulting array is a mapping from id => "native data" where - * the definition of "native data" is only of relevance to the given item. + * @return array The nature of the resulting array is a mapping from id => "native data" where + * the definition of "native data" is only of relevance to the given item. */ public function getDataFor($arrIds); /** * Remove values for items. * - * @param string[] $arrIds The ids of the items to retrieve. + * @param list $arrIds The ids of the items to retrieve. * * @return void */ diff --git a/src/Attribute/ISimple.php b/src/Attribute/ISimple.php index 9724950fe..4614acfb3 100644 --- a/src/Attribute/ISimple.php +++ b/src/Attribute/ISimple.php @@ -32,6 +32,8 @@ interface ISimple extends IAttribute * Returns the SQL primitive type declaration in MySQL notation. i.e. "text NULL". * * @return string + * + * @deprecated Implement schema generators instead - see #1267. */ public function getSQLDataType(); @@ -39,6 +41,8 @@ public function getSQLDataType(); * Creates the underlying database structure for this attribute. * * @return void + * + * @deprecated Implement schema generators instead - see #1267. */ public function createColumn(); @@ -46,6 +50,8 @@ public function createColumn(); * Removes the underlying database structure for this attribute. * * @return void + * + * @deprecated Implement schema generators instead - see #1267. */ public function deleteColumn(); @@ -55,6 +61,8 @@ public function deleteColumn(); * @param string $strNewColumnName The new column name for the attribute. * * @return void + * + * @deprecated Implement schema generators instead - see #1267. */ public function renameColumn($strNewColumnName); @@ -72,7 +80,7 @@ public function unserializeData($value); * * @param mixed $value The input value. * - * @return string + * @return string|null */ public function serializeData($value); } diff --git a/src/Attribute/ITranslated.php b/src/Attribute/ITranslated.php index 84aa3a415..144f2955f 100644 --- a/src/Attribute/ITranslated.php +++ b/src/Attribute/ITranslated.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -39,21 +40,19 @@ public function getMetaModel(); /** * Search matches for the given expression. * - * @param string $strPattern The text to search for. This may contain wildcards. + * @param string $strPattern The text to search for. This may contain wildcards. + * @param list $arrLanguages Array of valid language codes that shall be searched. + * (optional) If empty, all languages will be taken into account. * - * @param array $arrLanguages Array of valid language codes that shall be searched. (optional) - * If empty, all languages will be taken into account. - * - * @return string[] the ids of matching items. + * @return list the ids of matching items. */ - public function searchForInLanguages($strPattern, $arrLanguages = array()); + public function searchForInLanguages($strPattern, $arrLanguages = []); /** * Set a value for an item in a certain language. * - * @param mixed[] $arrValues The values to be set in id => value layout. - * - * @param string $strLangCode The language code for which the data shall be retrieved. + * @param array> $arrValues The values to be set in id => value layout. + * @param string $strLangCode The language code for which the data shall be retrieved. * * @return void */ @@ -62,20 +61,18 @@ public function setTranslatedDataFor($arrValues, $strLangCode); /** * Get values for the given items in a certain language. * - * @param string[] $arrIds The ids for which values shall be retrieved. + * @param list $arrIds The ids for which values shall be retrieved. + * @param string $strLangCode The language code for which the data shall be retrieved. * - * @param string $strLangCode The language code for which the data shall be retrieved. - * - * @return mixed[] the values. + * @return array> the values. */ public function getTranslatedDataFor($arrIds, $strLangCode); /** * Remove values for items in a certain language. * - * @param string[] $arrIds The ids for which values shall be removed. - * - * @param string $strLangCode The language code for which the data shall be removed. + * @param list $arrIds The ids for which values shall be removed. + * @param string $strLangCode The language code for which the data shall be removed. * * @return void */ diff --git a/src/Attribute/ManagedAttributeTrait.php b/src/Attribute/ManagedAttributeTrait.php new file mode 100644 index 000000000..235dbd107 --- /dev/null +++ b/src/Attribute/ManagedAttributeTrait.php @@ -0,0 +1,58 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\Attribute; + +use Contao\System; + +trait ManagedAttributeTrait +{ + private function isManagedAttribute(string $type): bool + { + $container = System::getContainer(); + $parameter = []; + if ($container->hasParameter('metamodels.managed-schema-type-names')) { + $parameter = $container->getParameter('metamodels.managed-schema-type-names'); + assert(\is_array($parameter)); + } + return \in_array($type, $parameter, true); + } + + private function triggerDeprecationIsUnmanagedAttribute(string $class, string $method): void + { + // @codingStandardsIgnoreStart + @trigger_error( + 'Class "' . $class . '" should be changed to a managed attribute and skip calling method "' . $method . + '".', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + + private function triggerDeprecationShouldNotCallManaged(string $class, string $method): void + { + // @codingStandardsIgnoreStart + @trigger_error( + 'Class "' . $class . '" is a managed attribute you should not call "' . $method . '".', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } +} diff --git a/src/Attribute/TranslatedReference.php b/src/Attribute/TranslatedReference.php index 3cf65f342..900b999e1 100644 --- a/src/Attribute/TranslatedReference.php +++ b/src/Attribute/TranslatedReference.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,7 +19,7 @@ * @author Sven Baumann * @author David Molineus * @author Andreas Fischer - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,14 +27,16 @@ namespace MetaModels\Attribute; use Contao\System; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; -use MetaModels\Filter\Rules\SimpleQuery; use MetaModels\IMetaModel; use MetaModels\ITranslatedMetaModel; /** * This is the MetaModelAttribute class for handling translated attributes that reference another table. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ abstract class TranslatedReference extends BaseComplex implements ITranslated { @@ -50,11 +52,11 @@ abstract class TranslatedReference extends BaseComplex implements ITranslated * * Note that you should not use this directly but use the factory classes to instantiate attributes. * - * @param IMetaModel $objMetaModel The MetaModel instance this attribute belongs to. - * @param array $arrData The information array, for attribute information, refer to documentation of - * table tl_metamodel_attribute and documentation of the certain attribute classes - * for information what values are understood. - * @param Connection $connection Database connection. + * @param IMetaModel $objMetaModel The MetaModel instance this attribute belongs to. + * @param array $arrData The information array, for attribute information, refer to documentation of + * table tl_metamodel_attribute and documentation of the certain attribute + * classes for information what values are understood. + * @param Connection|null $connection Database connection. */ public function __construct(IMetaModel $objMetaModel, $arrData = [], Connection $connection = null) { @@ -68,8 +70,8 @@ public function __construct(IMetaModel $objMetaModel, $arrData = [], Connection ); // @codingStandardsIgnoreEnd $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); } - $this->connection = $connection; } @@ -83,54 +85,52 @@ abstract protected function getValueTable(); /** * Build a where clause for the given id(s) and language code. * - * @param QueryBuilder $queryBuilder The query builder for the query being build. - * @param string[]|string|null $mixIds One, none or many ids to use. - * @param string|string[] $mixLangCode The language code/s to use, optional. + * @param QueryBuilder $queryBuilder The query builder for the query being build. + * @param list|string|null $mixIds One, none or many ids to use. + * @param list $mixLangCode The language code/s to use, optional. * * @return void */ - private function buildWhere(QueryBuilder $queryBuilder, $mixIds, $mixLangCode = '') - { - $alias = ''; - if (null !== $firstFromAlias = $queryBuilder->getQueryPart('from')[0]['alias']) { - $alias = $firstFromAlias . '.'; - } - + private function buildWhere( + QueryBuilder $queryBuilder, + array|string|null $mixIds, + array $mixLangCode, + string $alias + ): void { $queryBuilder - ->andWhere($alias . 'att_id = :att_id') + ->andWhere($alias . '.att_id = :att_id') ->setParameter('att_id', $this->get('id')); - if (!empty($mixIds)) { - if (is_array($mixIds)) { - $queryBuilder - ->andWhere($alias . 'item_id IN (:item_ids)') - ->setParameter('item_ids', $mixIds, Connection::PARAM_STR_ARRAY); + if (null !== $mixIds) { + if (\is_array($mixIds)) { + if ([] === $mixIds) { + $queryBuilder + ->andWhere('1=0'); + } else { + $queryBuilder + ->andWhere($alias . '.item_id IN (:item_ids)') + ->setParameter('item_ids', $mixIds, ArrayParameterType::STRING); + } } else { $queryBuilder - ->andWhere($alias . 'item_id = :item_id') + ->andWhere($alias . '.item_id = :item_id') ->setParameter('item_id', $mixIds); } } - if (!empty($mixLangCode)) { - if (is_array($mixLangCode)) { - $queryBuilder - ->andWhere($alias . 'langcode IN (:langcode)') - ->setParameter('langcode', $mixLangCode, Connection::PARAM_STR_ARRAY); - } else { - $queryBuilder - ->andWhere($alias . 'langcode = :langcode') - ->setParameter('langcode', $mixLangCode); - } + if ([] !== $mixLangCode) { + $queryBuilder + ->andWhere($alias . '.langcode IN (:langcode)') + ->setParameter('langcode', $mixLangCode, ArrayParameterType::STRING); } } /** * Retrieve the values to be used in the INSERT or UPDATE SQL for the given parameters. * - * @param array $arrValue The native value of the attribute. - * @param int $intId The id of the item to be saved. - * @param string $strLangCode The language code of the language the value is in. + * @param array{value: mixed, ...} $arrValue The native value of the attribute. + * @param string $intId The id of the item to be saved. + * @param string $strLangCode The language code of the language the value is in. * * @return array * @@ -138,18 +138,22 @@ private function buildWhere(QueryBuilder $queryBuilder, $mixIds, $mixLangCode = */ protected function getSetValues($arrValue, $intId, $strLangCode) { - if (($arrValue !== null) && !is_array($arrValue)) { - throw new \InvalidArgumentException(sprintf('Invalid value provided: %s', var_export($arrValue, true))); + /** + * @psalm-suppress DocblockTypeContradiction + * @psalm-suppress RedundantConditionGivenDocblockType + * Remove when we have strict type hints in the method signature. + */ + if (($arrValue === null) || !\is_array($arrValue)) { + throw new \InvalidArgumentException(\sprintf('Invalid value provided: %s', \var_export($arrValue, true))); } - return array - ( - 'tstamp' => time(), - 'value' => (string) $arrValue['value'], - 'att_id' => $this->get('id'), + return [ + 'tstamp' => \time(), + 'value' => (string) $arrValue['value'], + 'att_id' => $this->get('id'), 'langcode' => $strLangCode, - 'item_id' => $intId, - ); + 'item_id' => $intId, + ]; } /** @@ -161,10 +165,10 @@ protected function getSetValues($arrValue, $intId, $strLangCode) */ protected function getOptionizer() { - return array( - 'key' => 'value', + return [ + 'key' => 'value', 'value' => 'value' - ); + ]; } @@ -173,7 +177,7 @@ protected function getOptionizer() */ public function valueToWidget($varValue) { - return $varValue['value']; + return $varValue['value'] ?? null; } /** @@ -183,12 +187,11 @@ public function valueToWidget($varValue) */ public function widgetToValue($varValue, $itemId) { - return array - ( - 'tstamp' => time(), + return [ + 'tstamp' => \time(), 'value' => $varValue, 'att_id' => $this->get('id'), - ); + ]; } /** @@ -196,35 +199,38 @@ public function widgetToValue($varValue, $itemId) */ public function getDataFor($arrIds) { - $strActiveLanguage = $this->getActiveLanguage(); + /** @psalm-suppress DeprecatedMethod */ + $strActiveLanguage = $this->getActiveLanguage(); + /** @psalm-suppress DeprecatedMethod */ $strFallbackLanguage = $this->getFallbackLanguage(); $arrReturn = $this->getTranslatedDataFor($arrIds, $strActiveLanguage); // Second round, fetch fallback languages if not all items could be resolved. - if ((count($arrReturn) < count($arrIds)) && ($strActiveLanguage != $strFallbackLanguage)) { - $arrFallbackIds = array(); + if (($strActiveLanguage !== $strFallbackLanguage) && (\count($arrReturn) < \count($arrIds))) { + $arrFallbackIds = []; foreach ($arrIds as $intId) { - if (empty($arrReturn[$intId])) { + if (!\array_key_exists($intId, $arrReturn)) { $arrFallbackIds[] = $intId; } } if ($arrFallbackIds) { - $arrFallbackData = $this->getTranslatedDataFor($arrFallbackIds, $strFallbackLanguage); + $arrFallbackData = $this->getTranslatedDataFor($arrFallbackIds, $strFallbackLanguage ?? ''); // Cannot use array_merge here as it would renumber the keys. foreach ($arrFallbackData as $intId => $arrValue) { $arrReturn[$intId] = $arrValue; } } } + return $arrReturn; } /** * Determine the available languages. * - * @return null|\string[] + * @return list * * @throws \RuntimeException When an untranslated MetaModel is encountered. */ @@ -235,12 +241,14 @@ private function determineLanguages() return $metaModel->getLanguages(); } + /** @psalm-suppress DeprecatedMethod */ $languages = $this->getMetaModel()->getAvailableLanguages(); if ($languages === null) { throw new \RuntimeException( 'MetaModel ' . $this->getMetaModel()->getName() . ' does not seem to be translated.' ); } + return $languages; } @@ -269,17 +277,20 @@ public function unsetDataFor($arrIds) */ public function searchFor($strPattern) { - return $this->searchForInLanguages($strPattern, array($this->getActiveLanguage())); + return $this->searchForInLanguages($strPattern, [$this->getActiveLanguage()]); } /** * {@inheritDoc} */ - public function searchForInLanguages($strPattern, $arrLanguages = array()) + public function searchForInLanguages($strPattern, $arrLanguages = []) { - $optionizer = $this->getOptionizer(); + if (empty($optionizer = $this->getOptionizer())) { + return []; + } + $procedure = 't.' . $optionizer['value'] . ' LIKE :pattern'; - $strPattern = str_replace(['*', '?'], ['%', '_'], $strPattern); + $strPattern = \str_replace(['*', '?'], ['%', '_'], $strPattern); $queryBuilder = $this->connection->createQueryBuilder() ->select('DISTINCT t.item_id') @@ -287,11 +298,12 @@ public function searchForInLanguages($strPattern, $arrLanguages = array()) ->andWhere($procedure) ->setParameter('pattern', $strPattern); - $this->buildWhere($queryBuilder, null, $arrLanguages); + $this->buildWhere($queryBuilder, null, $arrLanguages, 't'); - $filterRule = SimpleQuery::createFromQueryBuilder($queryBuilder, 'item_id'); + $statement = $queryBuilder->executeQuery(); - return $filterRule->getMatchingIds(); + // 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()); } /** @@ -299,42 +311,33 @@ public function searchForInLanguages($strPattern, $arrLanguages = array()) */ public function sortIds($idList, $strDirection) { - $langSet = sprintf( - '\'%s\',\'%s\'', - $this->getActiveLanguage(), - $this->getFallbackLanguage() - ); - - $statement = $this->connection - ->executeQuery( - sprintf( - 'SELECT t1.item_id - FROM %1$s AS t1 - INNER JOIN %1$s as t3 ON (t1.id = (SELECT - t2.id - FROM %1$s AS t2 - WHERE (t2.att_id=%2$s) - AND langcode IN (%3$s) - AND (t2.item_id=t1.item_id) - ORDER BY FIELD(t2.langcode,%3$s) - LIMIT 1 - )) - WHERE (t1.item_id IN (?)) - AND (t3.item_id IN (?)) - GROUP BY t1.id - ORDER BY t1.value %4$s', - // @codingStandardsIgnoreStart - we want to keep the numbers at the end of the lines below. - $this->getValueTable(), // 1 - $this->get('id'), // 2 - $langSet, // 3 - $strDirection // 4 - // @codingStandardsIgnoreEnd - ), - [$idList, $idList], - [Connection::PARAM_STR_ARRAY, Connection::PARAM_STR_ARRAY] - ); - - return $statement->fetchAll(\PDO::FETCH_COLUMN, 0); + $queryBuilder = $this->connection->createQueryBuilder(); + $expr = $queryBuilder->expr(); + $queryBuilder + ->select('IF(t2.item_id IS NOT NULL, t2.item_id, t1.item_id)') + ->from($this->getValueTable(), 't1') + ->leftJoin( + 't1', + $this->getValueTable(), + 't2', + (string) $expr->and( + $expr->eq('t1.att_id', 't2.att_id'), + $expr->eq('t1.item_id', 't2.item_id'), + $expr->eq('t2.langcode', ':langcode'), + ) + ) + ->where($expr->eq('t1.att_id', ':att_id')) + ->andWhere($expr->in('t1.item_id', ':id_list')) + ->andWhere($expr->in('t1.langcode', ':langfallbackcode')) + ->setParameter('langcode', $this->getActiveLanguage()) + ->setParameter('langfallbackcode', $this->getFallbackLanguage()) + ->setParameter('att_id', $this->get('id')) + ->setParameter('id_list', \array_unique($idList), ArrayParameterType::STRING); + + $statement = $queryBuilder->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()); } /** @@ -346,14 +349,14 @@ public function getFilterOptions($idList, $usedOnly, &$arrCount = null) ->select('t.*') ->from($this->getValueTable(), 't'); - $this->buildWhere($queryBuilder, $idList, $this->getActiveLanguage()); + $this->buildWhere($queryBuilder, $idList, [$this->getActiveLanguage()], 't'); - $statement = $queryBuilder->execute(); + $statement = $queryBuilder->executeQuery(); $arrOptionizer = $this->getOptionizer(); $arrReturn = []; - while ($objValue = $statement->fetch(\PDO::FETCH_OBJ)) { - $arrReturn[$objValue->{$arrOptionizer['key']}] = $objValue->{$arrOptionizer['value']}; + while ($objValue = $statement->fetchAssociative()) { + $arrReturn[$objValue[$arrOptionizer['key']]] = $objValue[$arrOptionizer['value']]; } return $arrReturn; } @@ -363,64 +366,66 @@ public function getFilterOptions($idList, $usedOnly, &$arrCount = null) */ public function setTranslatedDataFor($arrValues, $strLangCode) { + if ('' === $strLangCode) { + throw new \InvalidArgumentException('Empty language code provided.'); + } // First off determine those to be updated and those to be inserted. - $arrIds = array_keys($arrValues); + $arrIds = \array_keys($arrValues); $arrExisting = $this->fetchExistingIdsFor($arrIds, $strLangCode); - $arrNewIds = array_diff($arrIds, $arrExisting); + $arrNewIds = \array_diff($arrIds, $arrExisting); + $tableAlias = $this->getValueTable(); // Update existing values - delete if empty. foreach ($arrExisting as $intId) { $queryBuilder = $this->connection->createQueryBuilder(); - $this->buildWhere($queryBuilder, $intId, $strLangCode); - - if ($arrValues[$intId]['value'] != '') { - $queryBuilder->update($this->getValueTable(), 't'); + $this->buildWhere($queryBuilder, $intId, [$strLangCode], $tableAlias); + $itemValues = $arrValues[$intId] ?? []; + if ($this->isValidItemValue($itemValues)) { + $queryBuilder->update($this->getValueTable()); - foreach ($this->getSetValues($arrValues[$intId], $intId, $strLangCode) as $name => $value) { + foreach ($this->getSetValues($itemValues, $intId, $strLangCode) as $name => $value) { $queryBuilder - ->set('t.' . $name, ':' . $name) + ->set($tableAlias . '.' . $name, ':' . $name) ->setParameter($name, $value); } } else { $queryBuilder->delete($this->getValueTable()); } - - $queryBuilder->execute(); + $queryBuilder->executeQuery(); } // Insert the new values. foreach ($arrNewIds as $intId) { - if ($arrValues[$intId]['value'] == '') { + $itemValues = $arrValues[$intId] ?? []; + if (!$this->isValidItemValue($itemValues)) { continue; } - $this->connection->insert( - $this->getValueTable(), - $this->getSetValues($arrValues[$intId], $intId, $strLangCode) - ); + $this->connection->insert($this->getValueTable(), $this->getSetValues($itemValues, $intId, $strLangCode)); } } /** * Filter the item ids for ids that exist in the database. * - * @param array $idList The id list. - * @param string $langCode The language code. + * @param list $idList The id list. + * @param string $langCode The language code. * * @return string[] */ protected function fetchExistingIdsFor($idList, $langCode) { + if ('' === $langCode) { + throw new \InvalidArgumentException('Empty language code provided.'); + } $queryBuilder = $this ->connection ->createQueryBuilder() ->select('t.item_id') ->from($this->getValueTable(), 't'); - $this->buildWhere($queryBuilder, $idList, $langCode); - - $statement = $queryBuilder->execute(); + $this->buildWhere($queryBuilder, $idList, [$langCode], 't'); - return $statement->fetchAll(\PDO::FETCH_COLUMN); + return $queryBuilder->executeQuery()->fetchFirstColumn(); } /** @@ -428,18 +433,21 @@ protected function fetchExistingIdsFor($idList, $langCode) */ public function getTranslatedDataFor($arrIds, $strLangCode) { + if ('' === $strLangCode) { + throw new \InvalidArgumentException('Empty language code provided.'); + } $queryBuilder = $this->connection->createQueryBuilder() ->select('t.*') ->from($this->getValueTable(), 't'); - $this->buildWhere($queryBuilder, $arrIds, $strLangCode); + $this->buildWhere($queryBuilder, $arrIds, [$strLangCode], 't'); - $statement = $queryBuilder->execute(); + $statement = $queryBuilder->executeQuery(); $arrReturn = []; - while ($value = $statement->fetch(\PDO::FETCH_ASSOC)) { - /** @noinspection PhpUndefinedFieldInspection */ + while ($value = $statement->fetchAssociative()) { $arrReturn[$value['item_id']] = $value; } + return $arrReturn; } @@ -448,39 +456,57 @@ public function getTranslatedDataFor($arrIds, $strLangCode) */ public function unsetValueFor($arrIds, $strLangCode) { + if ('' === $strLangCode) { + throw new \InvalidArgumentException('Empty language code provided.'); + } $queryBuilder = $this->connection->createQueryBuilder()->delete($this->getValueTable()); - $this->buildWhere($queryBuilder, $arrIds, $strLangCode); + $this->buildWhere($queryBuilder, $arrIds, [$strLangCode], $this->getValueTable()); - $queryBuilder->execute(); + $queryBuilder->executeQuery(); } /** * Retrieve the current language of the MetaModel we are attached to. * - * @return string + * @return non-empty-string */ - private function getActiveLanguage() + private function getActiveLanguage(): string { $metaModel = $this->getMetaModel(); if (!$metaModel instanceof ITranslatedMetaModel) { - return $metaModel->getActiveLanguage(); + /** @psalm-suppress DeprecatedMethod */ + $activeLanguage = $metaModel->getActiveLanguage(); + assert('' !== $activeLanguage); + + return $activeLanguage; } + $activeLanguage = $metaModel->getLanguage(); + assert('' !== $activeLanguage); - return $metaModel->getLanguage(); + return $activeLanguage; } /** * Retrieve the main language of the MetaModel we are attached to. * - * @return string + * @return string|null */ - private function getFallbackLanguage() + private function getFallbackLanguage(): ?string { $metaModel = $this->getMetaModel(); if (!$metaModel instanceof ITranslatedMetaModel) { + /** @psalm-suppress DeprecatedMethod */ return $metaModel->getFallbackLanguage(); } return $metaModel->getMainLanguage(); } + + /** @psalm-assert-if-true array{value: non-empty-string, ...} $itemValues */ + private function isValidItemValue(array $itemValues): bool + { + return \array_key_exists('value', $itemValues) + && \is_string($itemValues['value']) + && '' !== $itemValues['value']; + } } diff --git a/src/BackendIntegration/InputScreen/IInputScreen.php b/src/BackendIntegration/InputScreen/IInputScreen.php index 43a06f441..921316cb8 100644 --- a/src/BackendIntegration/InputScreen/IInputScreen.php +++ b/src/BackendIntegration/InputScreen/IInputScreen.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Stefan Heimes * @author Alexander Menk * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +29,8 @@ /** * This interface describes the abstraction of an input screen. * + * @psalm-type TLegend=array{name: string, visible: bool, properties: list} + * * @deprecated This will get removed. */ interface IInputScreen @@ -42,14 +45,14 @@ public function getId(); /** * Retrieve all legends. * - * @return string[] + * @return array */ public function getLegends(); /** * Retrieve the names of all legends. * - * @return string[] + * @return list */ public function getLegendNames(); @@ -58,7 +61,7 @@ public function getLegendNames(); * * @param string $name The name of the legend. * - * @return array + * @return TLegend */ public function getLegend($name); @@ -81,7 +84,7 @@ public function getProperty($name); /** * Retrieve the names of all contained properties. * - * @return string[] + * @return list */ public function getPropertyNames(); diff --git a/src/BackendIntegration/InputScreen/InputScreen.php b/src/BackendIntegration/InputScreen/InputScreen.php index 9ccfa0460..3e787a72a 100644 --- a/src/BackendIntegration/InputScreen/InputScreen.php +++ b/src/BackendIntegration/InputScreen/InputScreen.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,7 +18,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,20 +32,31 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionChain; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; use MetaModels\Events\CreatePropertyConditionEvent; +use MetaModels\Helper\LocaleUtil; use MetaModels\IMetaModel; use MetaModels\IMetaModelsServiceContainer; use MetaModels\ITranslatedMetaModel; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Implementation of IInputScreen. * + * @psalm-import-type TLegend from IInputScreen + * * @deprecated This class will get removed. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @psalm-suppress DeprecatedInterface */ class InputScreen implements IInputScreen { /** * The service container. * + * @psalm-suppress DeprecatedInterface + * * @var IMetaModelsServiceContainer */ protected $container; @@ -60,57 +71,55 @@ class InputScreen implements IInputScreen /** * The legends contained within the input screen. * - * @var array + * @var array */ - protected $legends = array(); + protected $legends = []; /** * The properties contained within the input screen. * * @var array */ - protected $properties = array(); + protected $properties = []; /** * The conditions. * * @var ConditionChainInterface[] */ - protected $conditions = array(); + protected $conditions = []; /** * Grouping and sorting information. * - * @var IInputScreenGroupingAndSorting[] + * @var list */ - protected $groupSort = array(); + protected $groupSort = []; /** * Simple map from property setting id to property name. * * @var array */ - protected $propertyMap = array(); + protected $propertyMap = []; /** * Simple map from property name to property setting id. * * @var array */ - protected $propertyMap2 = array(); + protected $propertyMap2 = []; /** * Create a new instance. * * @param IMetaModelsServiceContainer $container The service container. - * * @param array $data The information about the input screen. - * * @param array $propertyRows The information about all contained properties. - * * @param array $conditions The property condition information. - * * @param array $groupSort The grouping and sorting information. + * + * @psalm-suppress DeprecatedInterface */ public function __construct($container, $data, $propertyRows, $conditions, $groupSort) { @@ -126,7 +135,6 @@ public function __construct($container, $data, $propertyRows, $conditions, $grou * Transform a legend information into the property legends. * * @param array $legend The legend to transform. - * * @param IMetaModel $metaModel The metamodel the legend belongs to. * * @return string @@ -138,20 +146,19 @@ public function __construct($container, $data, $propertyRows, $conditions, $grou protected function translateLegend($legend, $metaModel) { $arrLegend = StringUtil::deserialize($legend['legendtitle']); - if (is_array($arrLegend)) { - $strLegend = $this->extractLegendName($arrLegend); + if (\is_array($arrLegend)) { + $strLegend = $this->extractLegendName($arrLegend, $metaModel); } else { - $strLegend = $legend['legendtitle'] ? $legend['legendtitle'] : 'legend'; + $strLegend = $legend['legendtitle'] ?: 'legend'; } $legendName = StringUtil::standardize($strLegend); - $this->legends[$legendName] = array - ( + $this->legends[$legendName] = [ 'name' => $strLegend, 'visible' => !(isset($legend['legendhide']) && (bool) $legend['legendhide']), - 'properties' => array() - ); + 'properties' => [] + ]; return $legendName; } @@ -170,14 +177,15 @@ protected function translateLegend($legend, $metaModel) private function extractLegendName(array $legend, IMetaModel $metaModel): string { // Current backend language. - $language = \str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + $language = LocaleUtil::formatAsLocale($GLOBALS['TL_LANGUAGE']); if (null !== ($result = $legend[$language] ?? null)) { return $result; } // Is it a regional locale? - if (false !== strpos($language, '_')) { - $chunks = explode('_', $language); - $language = array_shift($chunks); + if (\str_contains($language, '_')) { + $chunks = \explode('_', $language); + $language = \array_shift($chunks); unset($chunks); if (null !== ($result = $legend[$language] ?? null)) { return $result; @@ -186,33 +194,32 @@ private function extractLegendName(array $legend, IMetaModel $metaModel): string // Try fallback language then. if ($metaModel instanceof ITranslatedMetaModel) { - if (null !== ($result = $legend[$metaModel->getMainLanguage()] ?? null)) { + if (null !== ($result = ($legend[$metaModel->getMainLanguage()] ?? null))) { return $result; } } else { - if (null !== ($result = $legend[$metaModel->getFallbackLanguage()] ?? null)) { + /** @psalm-suppress DeprecatedMethod */ + if (null !== ($result = ($legend[(string) $metaModel->getFallbackLanguage()] ?? null))) { return $result; } } // Last resort, simply "legend". See issue #926. - return 'legend' . (count($this->legends) + 1); + return 'legend' . (\count($this->legends) + 1); } /** * Translate a property. * * @param array $property The property information to transform. - * * @param IMetaModel $metaModel The MetaModel the property belongs to. - * * @param string $legend The legend the property belongs to. * * @return bool */ protected function translateProperty($property, $metaModel, $legend) { - $attribute = $metaModel->getAttributeById($property['attr_id']); + $attribute = $metaModel->getAttributeById((int) $property['attr_id']); // Dead meat. if (!$attribute) { @@ -223,10 +230,9 @@ protected function translateProperty($property, $metaModel, $legend) $this->legends[$legend]['properties'][] = $propName; - $this->properties[$propName] = array - ( - 'info' => $attribute->getFieldDefinition($property), - ); + $this->properties[$propName] = [ + 'info' => $attribute->getFieldDefinition($property), + ]; return true; } @@ -267,19 +273,19 @@ protected function translateRows($rows) { $metaModel = $this->getMetaModel(); $activeLegend = $this->translateLegend( - array('legendtitle' => $metaModel->getName(), 'legendhide' => false), + ['legendtitle' => $metaModel->getName(), 'legendhide' => false], $metaModel ); $activeLegendId = null; // First pass, fetch all attribute names. - $columnNames = array(); + $columnNames = []; foreach ($rows as $row) { if ($row['dcatype'] != 'attribute') { continue; } - $attribute = $metaModel->getAttributeById($row['attr_id']); + $attribute = $metaModel->getAttributeById((int) $row['attr_id']); if ($attribute) { $columnNames[$row['id']] = $attribute->getColName(); } @@ -299,7 +305,7 @@ protected function translateRows($rows) case 'attribute': $exists = $this->translateProperty($row, $metaModel, $activeLegend); - if ($exists && $activeLegendId) { + if ($exists && null !== $activeLegendId) { $this->applyLegendConditions($row['id'], $activeLegendId); } @@ -323,19 +329,25 @@ protected function translateRows($rows) protected function transformCondition($condition) { $dispatcher = System::getContainer()->get('event_dispatcher'); - $event = new CreatePropertyConditionEvent($condition, $this->getMetaModel()); + assert($dispatcher instanceof EventDispatcherInterface); - /** @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher */ + /** @psalm-suppress DeprecatedClass */ + $event = new CreatePropertyConditionEvent($condition, $this->getMetaModel()); + + /** + * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface $dispatcher + * @psalm-suppress DeprecatedClass + */ $dispatcher->dispatch($event, CreatePropertyConditionEvent::NAME); - if ($event->getInstance() === null) { - throw new \RuntimeException(sprintf( + if (($instance = $event->getInstance()) === null) { + throw new \RuntimeException(\sprintf( 'Condition of type %s could not be transformed to an instance.', $condition['type'] )); } - return $event->getInstance(); + return $instance; } /** @@ -348,14 +360,14 @@ protected function transformCondition($condition) protected function transformConditions($conditions) { // First pass, sort them into pid. - $sorted = array(); - $byPid = array(); - foreach ($conditions as $i => $condition) { - $sorted[$condition['id']] = $conditions[$i]; + $sorted = []; + $byPid = []; + foreach ($conditions as $condition) { + $sorted[$condition['id']] = $condition; $byPid[$condition['pid']][] = $condition['id']; } - $instances = array(); + $instances = []; // Second pass, handle them. foreach ($sorted as $id => $condition) { $instances[$id] = $this->transformCondition($condition); @@ -370,7 +382,7 @@ protected function transformConditions($conditions) } $result = $this->conditions[$settingId]; $condition = $instances[$id]; - $parent = ($pid == 0) ? $result : $instances[$pid]; + $parent = ($pid === 0) ? $result : $instances[$pid]; // have other classes in the future. if ($parent instanceof ConditionChainInterface) { @@ -401,7 +413,7 @@ protected function transformGroupSort($rows) */ public function getId() { - return $this->data['id']; + return (int) $this->data['id']; } /** @@ -417,7 +429,7 @@ public function getLegends() */ public function getLegendNames() { - return array_keys($this->legends); + return \array_keys($this->legends); } /** @@ -441,7 +453,7 @@ public function getProperties() */ public function getProperty($name) { - return isset($this->properties[$name]) ? $this->properties[$name] : null; + return $this->properties[$name]; } /** @@ -449,9 +461,9 @@ public function getProperty($name) */ public function getPropertyNames() { - $result = array(); + $result = []; foreach ($this->getLegends() as $legend) { - $result = array_merge($result, $legend['properties']); + $result = \array_merge($result, $legend['properties']); } return $result; @@ -463,7 +475,8 @@ public function getPropertyNames() public function getConditionsFor($name) { $property = $this->propertyMap2[$name]; - return isset($this->conditions[$property]) ? $this->conditions[$property] : null; + + return $this->conditions[$property] ?? null; } /** @@ -481,12 +494,14 @@ public function getGroupingAndSorting() */ public function getMetaModel() { + /** @psalm-suppress DocblockTypeContradiction */ if (null === $this->data) { throw new \RuntimeException( 'No input screen data available, did you forget to define the view combinations?' ); } + /** @psalm-suppress DeprecatedMethod */ $factory = $this->container->getFactory(); $metaModel = $factory->getMetaModel($factory->translateIdToMetaModelName($this->data['pid'])); @@ -507,7 +522,7 @@ public function getIcon() return $this->data['backendicon']; } - return null; + return ''; } /** @@ -515,7 +530,7 @@ public function getIcon() */ public function getBackendSection() { - return trim($this->data['backendsection']); + return trim($this->data['backendsection'] ?? ''); } /** @@ -523,7 +538,7 @@ public function getBackendSection() */ public function getBackendCaption() { - return StringUtil::deserialize($this->data['backendcaption'], true); + return StringUtil::deserialize($this->data['backendcaption'] ?? [], true); } /** @@ -531,7 +546,7 @@ public function getBackendCaption() */ public function getParentTable() { - return $this->data['ptable']; + return $this->data['ptable'] ?? null; } /** @@ -553,7 +568,7 @@ public function getRenderMode() return 'hierarchical'; } - return $this->data['rendermode']; + return $this->data['rendermode'] ?? ''; } /** @@ -609,7 +624,7 @@ public function isDeletable() */ public function getPanelLayout() { - return $this->data['panelLayout']; + return $this->data['panelLayout'] ?? ''; } /** diff --git a/src/BackendIntegration/InputScreen/InputScreenGroupingAndSorting.php b/src/BackendIntegration/InputScreen/InputScreenGroupingAndSorting.php index 4de9e4ae3..e73fbee3c 100644 --- a/src/BackendIntegration/InputScreen/InputScreenGroupingAndSorting.php +++ b/src/BackendIntegration/InputScreen/InputScreenGroupingAndSorting.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2022 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Molineus * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2022 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,6 +31,8 @@ class InputScreenGroupingAndSorting implements IInputScreenGroupingAndSorting * The parenting input screen. * * @var IInputScreen + * + * @psalm-suppress DeprecatedInterface */ protected $inputScreen; @@ -46,6 +49,8 @@ class InputScreenGroupingAndSorting implements IInputScreenGroupingAndSorting * @param array $data The information about the input screen. * * @param IInputScreen $inputScreen The information about all contained properties. + * + * @psalm-suppress DeprecatedInterface */ public function __construct($data, IInputScreen $inputScreen) { @@ -78,7 +83,7 @@ public function getRenderGroupType() */ public function getRenderGroupLength() { - return (int) $this->data['rendergrouplen']; + return (string) $this->data['rendergrouplen']; } /** @@ -88,11 +93,9 @@ public function getRenderGroupAttribute() { if (!empty($this->data['rendergroupattr'])) { $metaModel = $this->getMetaModel(); - if ($metaModel) { - $attribute = $metaModel->getAttributeById($this->data['rendergroupattr']); - if ($attribute) { - return $attribute->getColName(); - } + $attribute = $metaModel->getAttributeById((int) $this->data['rendergroupattr']); + if ($attribute) { + return $attribute->getColName(); } } @@ -114,11 +117,9 @@ public function getRenderSortAttribute() { if (!empty($this->data['rendersortattr'])) { $metaModel = $this->getMetaModel(); - if ($metaModel) { - $attribute = $metaModel->getAttributeById($this->data['rendersortattr']); - if ($attribute) { - return $attribute->getColName(); - } + $attribute = $metaModel->getAttributeById((int) $this->data['rendersortattr']); + if ($attribute) { + return $attribute->getColName(); } } diff --git a/src/BackendIntegration/Module.php b/src/BackendIntegration/Module.php index 5e2d55118..8104802a3 100644 --- a/src/BackendIntegration/Module.php +++ b/src/BackendIntegration/Module.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,20 +15,25 @@ * @author Andreas Isaak * @author Stefan Heimes * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\BackendIntegration; +use Contao\Input; use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; use ContaoCommunityAlliance\DcGeneral\DataContainerInterface; use ContaoCommunityAlliance\DcGeneral\Contao\Callback\Callbacks; /** * Implementation of the MetaModel Backend Module that allowing access to MetaModel configuration etc. Everything below - * http://..../contao?do=metamodels&.... ends up here. + * https://..../contao/metamodels?.... ends up here. + * + * @deprecated Not in use anymore since 2.3. */ class Module { @@ -37,7 +42,7 @@ class Module * * @var DataContainerInterface */ - private $dataContainer; + private DataContainerInterface $dataContainer; /** * Create a new instance. @@ -62,15 +67,18 @@ public function generate() $GLOBALS['TL_CSS'][] = 'bundles/metamodelscore/css/style.css'; $arrModule = $GLOBALS['BE_MOD']['metamodels']['metamodels']; // Custom action (if key is not defined in config.php the default action will be called). - if (\Input::get('key') && isset($arrModule[\Input::get('key')])) { - Callbacks::call($arrModule[\Input::get('key')], $this, $arrModule); + if (Input::get('key') && isset($arrModule[Input::get('key')])) { + Callbacks::call($arrModule[Input::get('key')], $this, $arrModule); } - $act = \Input::get('act'); - if (!strlen($act)) { + $act = Input::get('act'); + if (!\strlen($act)) { $act = 'showAll'; } - return $this->dataContainer->getEnvironment()->getController()->handle(new Action($act)); + $controller = $this->dataContainer->getEnvironment()->getController(); + assert($controller instanceof ControllerInterface); + + return $controller->handle(new Action($act)); } } diff --git a/src/BackendIntegration/PurgeAssets.php b/src/BackendIntegration/PurgeAssets.php index a3508e4a0..d4683fcc5 100644 --- a/src/BackendIntegration/PurgeAssets.php +++ b/src/BackendIntegration/PurgeAssets.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 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. @@ -15,13 +15,15 @@ * @author Sven Baumann * @author Christian Schiffler * @author Richard Henkenjohann - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\BackendIntegration; +use Contao\CoreBundle\Monolog\ContaoContext; +use Contao\Folder; use Contao\System; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\System\LogEvent; @@ -44,13 +46,14 @@ public function purge() { foreach ($GLOBALS['TL_PURGE']['folders']['metamodels_assets']['affected'] as $folderName) { // Purge the folder - $folder = new \Folder($folderName); + $folder = new Folder($folderName); $folder->purge(); } $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); $dispatcher->dispatch( - new LogEvent('Purged the MetaModels assets', __METHOD__, TL_CRON), + new LogEvent('Purged the MetaModels assets', __METHOD__, ContaoContext::CRON), ContaoEvents::SYSTEM_LOG ); } diff --git a/src/BackendIntegration/PurgeCache.php b/src/BackendIntegration/PurgeCache.php index b6d527236..2ac6adbe0 100644 --- a/src/BackendIntegration/PurgeCache.php +++ b/src/BackendIntegration/PurgeCache.php @@ -75,7 +75,7 @@ public function purge() $this->logger->log( LogLevel::INFO, 'Purged the MetaModels cache', - ['contao' => new ContaoContext(__METHOD__, TL_CRON)] + ['contao' => new ContaoContext(__METHOD__, ContaoContext::CRON)] ); } } diff --git a/src/BackendIntegration/TemplateList.php b/src/BackendIntegration/TemplateList.php index d8ec2babf..4484f6e38 100644 --- a/src/BackendIntegration/TemplateList.php +++ b/src/BackendIntegration/TemplateList.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +23,14 @@ use Doctrine\DBAL\Connection; use Symfony\Component\Finder\Finder; +use Symfony\Component\Finder\SplFileInfo; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function array_replace_recursive; +use function array_unique; +use function implode; +use function is_dir; +use function ksort; /** * Handy helper class to retrieve a list of templates. @@ -34,14 +42,14 @@ class TemplateList * * @var Connection */ - private $database; + private Connection $database; /** * The resource directories. * * @var string[] */ - private $resourceDirs; + private array $resourceDirs; /** * The project root directory. @@ -50,18 +58,27 @@ class TemplateList */ private $rootDir; + /** + * The translator. + * + * @var TranslatorInterface + */ + private TranslatorInterface $translator; + /** * Create a new instance. * - * @param Connection $database The database connection. - * @param string[] $resourceDirs The resource directories. - * @param string $rootDir The root directory. + * @param Connection $database The database connection. + * @param string[] $resourceDirs The resource directories. + * @param string $rootDir The root directory. + * @param TranslatorInterface $translator The translator. */ - public function __construct(Connection $database, $resourceDirs, $rootDir) + public function __construct(Connection $database, $resourceDirs, $rootDir, TranslatorInterface $translator) { $this->database = $database; $this->resourceDirs = $resourceDirs; $this->rootDir = $rootDir; + $this->translator = $translator; } /** @@ -82,12 +99,15 @@ public function getTemplatesForBase($templateBaseName) $this->fetchTemplatesFromResourceDirectories($templateBaseName) ); - $templateList = array(); + $templateList = []; foreach ($allTemplates as $template => $themeList) { - $templateList[$template] = sprintf( - $GLOBALS['TL_LANG']['MSC']['template_in_theme'], - $template, - implode(', ', $themeList) + $templateList[$template] = $this->translator->trans( + 'template_in_theme', + [ + '%template%' => $template, + '%themes%' => implode(', ', $themeList) + ], + 'metamodels_default' ); } @@ -106,7 +126,7 @@ public function getTemplatesForBase($templateBaseName) */ private function getNoThemeMessage() { - return $GLOBALS['TL_LANG']['MSC']['no_theme']; + return $this->translator->trans('no_theme', [], 'metamodels_default'); } /** @@ -121,7 +141,8 @@ private function fetchRootTemplates($templateBaseName) return $this->getTemplatesForBaseFrom( $templateBaseName, $this->rootDir . '/templates', - $this->getNoThemeMessage() + $this->getNoThemeMessage(), + true ); } @@ -140,14 +161,14 @@ private function fetchTemplatesFromThemes($templateBaseName) ->createQueryBuilder() ->select('t.id, t.name, t.templates') ->from('tl_theme', 't') - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); // Add all the theme templates folders. foreach ($themes as $theme) { $templateDir = $theme['templates']; $themeName = $theme['name']; - if ($templateDir != '') { + if ($templateDir !== '') { $allTemplates = array_replace_recursive( $allTemplates, $this->getTemplatesForBaseFrom( @@ -188,15 +209,13 @@ private function fetchTemplatesFromResourceDirectories($templateBaseName) * Fetch a list of matching templates of the current base within the given folder and the passed theme name. * * @param string $base The base for the templates to be retrieved. - * * @param string $folder The folder to search in. - * * @param string $themeName The name of the theme for the given folder (will get used in the returned description * text). * * @return array */ - private function getTemplatesForBaseFrom($base, $folder, $themeName) + private function getTemplatesForBaseFrom($base, $folder, $themeName, bool $onlyOneLevel = false) { if (!is_dir($folder)) { return []; @@ -204,10 +223,13 @@ private function getTemplatesForBaseFrom($base, $folder, $themeName) $themeName = trim($themeName); $foundTemplates = Finder::create()->in($folder)->name($base . '*'); + if ($onlyOneLevel) { + $foundTemplates->depth('<1'); + } $templates = []; foreach ($foundTemplates as $template) { - /** @var \Symfony\Component\Finder\SplFileInfo $template */ + /** @var SplFileInfo $template */ $templates[$template->getBasename('.' . $template->getExtension())] = [$themeName => $themeName]; } diff --git a/src/BackendIntegration/ViewCombinations.php b/src/BackendIntegration/ViewCombinations.php index 7ea5b4d5e..e360c2c07 100644 --- a/src/BackendIntegration/ViewCombinations.php +++ b/src/BackendIntegration/ViewCombinations.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,6 +22,7 @@ namespace MetaModels\BackendIntegration; use Contao\Environment; +use Contao\System; /** * Class ViewCombinations. @@ -28,6 +30,8 @@ * Retrieve combinations of view and input screens for the currently logged in user (either frontend or backend). * * @deprecated This will get removed. + * + * @psalm-suppress DeprecatedClass */ class ViewCombinations extends \MetaModels\Helper\ViewCombinations { @@ -38,30 +42,30 @@ class ViewCombinations extends \MetaModels\Helper\ViewCombinations */ protected function authenticateUser() { - if (\System::getContainer()->get('cca.dc-general.scope-matcher')->currentScopeIsUnknown()) { + $scopeMatcher = System::getContainer()->get('cca.dc-general.scope-matcher'); + if (null === $scopeMatcher || $scopeMatcher->currentScopeIsUnknown()) { return false; } // Do not execute anything if we are on the login page because no User is logged in. - if (strpos(Environment::get('script'), 'contao/login') !== false) { + if (\str_contains(Environment::get('script'), 'contao/login')) { return false; } // Issue #66 - contao/install.php is not working anymore. Thanks to Stefan Lindecke (@lindesbs). - if (strpos(Environment::get('request'), 'install') !== false) { + if (\str_contains(Environment::get('request'), 'install')) { return false; } - if (strpos(Environment::get('script'), 'system/bin') !== false) { + if (\str_contains(Environment::get('script'), 'system/bin')) { return false; } // Bug fix: If the user is not authenticated, contao will redirect to contao/index.php - // But in this moment the TL_PATH is not defined, so the $this->Environment->request - // generate a url without replacing the basepath(TL_PATH) with an empty string. - $authResult = $this->getUser()->authenticate(); - - return ($authResult === true || $authResult === null) ? true : false; + // But at this moment the TL_PATH is not defined, so the $this->Environment->request + // generate an url without replacing the basepath(TL_PATH) with an empty string. + /** @psalm-suppress DeprecatedMethod */ + return $this->getUser()->authenticate(); } /** @@ -70,13 +74,14 @@ protected function authenticateUser() protected function getUserGroups() { // Try to get the group(s) - // there might be a NULL in there as BE admins have no groups and user might have one but it is not mandatory. + // there might be a NULL in there as BE admins have no groups and user might have one, but it is not mandatory. // I would prefer a default group for both, fe and be groups. + /** @psalm-suppress DeprecatedClass */ $groups = parent::getUserGroups(); /** @noinspection PhpUndefinedFieldInspection */ // Special case in combinations, admins have the implicit group id -1. - if ($this->getUser()->admin) { + if ((bool) $this->getUser()->admin) { $groups[] = -1; } diff --git a/src/CoreBundle/Assets/IconBuilder.php b/src/CoreBundle/Assets/IconBuilder.php index 1b9dca040..c7795cfc0 100644 --- a/src/CoreBundle/Assets/IconBuilder.php +++ b/src/CoreBundle/Assets/IconBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author David Molineus - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,9 +24,11 @@ use Contao\CoreBundle\Framework\Adapter; use Contao\CoreBundle\Image\ImageFactoryInterface; +use Contao\FilesModel; +use Contao\Image; use Contao\Validator; use Symfony\Component\Filesystem\Filesystem; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; /** * This class takes care of building icons for the backend. @@ -38,59 +40,59 @@ class IconBuilder * * @var string */ - private $rootPath; + private string $rootPath; /** * The output path for assets. * * @var string */ - private $outputPath; + private string $outputPath; /** * The web reachable path for assets. * * @var string */ - private $webPath; + private string $webPath; /** * The project web reachable path for assets. * * @var string */ - private $projectWebPath; + private string $projectWebPath; /** * Adapter to the Contao\FilesModel class. * - * @var \Contao\FilesModel|Adapter + * @var Adapter */ - private $filesAdapter; + private Adapter $filesAdapter; /** * The image factory. * * @var ImageFactoryInterface */ - private $imageFactory; + private ImageFactoryInterface $imageFactory; /** * The image adapter. * - * @var \Contao\Image|Adapter + * @var Adapter */ - private $image; + private Adapter $image; /** * Create a new instance. * - * @param Adapter $filesAdapter Adapter to the Contao files model class. + * @param Adapter $filesAdapter Adapter to the Contao files model class. * @param ImageFactoryInterface $imageFactory The image factory for resizing images. * @param string $rootPath The root path of the application. * @param string $outputPath The output path for assets. * @param string $webPath The web reachable path for assets. - * @param Adapter $imageAdapter The image adapter to generate HTML code images. + * @param Adapter $imageAdapter The image adapter to generate HTML code images. * @param string $projectWebPath The project web reachable path for assets. */ public function __construct( @@ -126,10 +128,10 @@ public function __construct( public function getBackendIcon($icon, $defaultIcon = 'bundles/metamodelscore/images/icons/metamodels.png') { $realIcon = $this->convertValueToPath($icon, $defaultIcon); - $targetPath = $this->outputPath . '/' . basename($realIcon); + $targetPath = $this->outputPath . '/' . \basename($realIcon); if (\file_exists($targetPath)) { - return $this->webPath . '/' . basename($realIcon); + return $this->webPath . '/' . \basename($realIcon); } if (!Path::isAbsolute($realIcon)) { @@ -138,7 +140,7 @@ public function getBackendIcon($icon, $defaultIcon = 'bundles/metamodelscore/ima $this->imageFactory->create($realIcon, [16, 16, 'center_center'], $targetPath); - return $this->webPath . '/' . basename($realIcon); + return $this->webPath . '/' . \basename($realIcon); } /** @@ -157,6 +159,7 @@ public function getBackendIconImageTag( $attributes = '', $defaultIcon = 'bundles/metamodelscore/images/icons/metamodels.png' ) { + /** @psalm-suppress InternalMethod - Class Adapter is internal, not the __call() method. Blame Contao. */ return $this->image->getHtml($this->getBackendIcon($icon, $defaultIcon), $alt, $attributes); } @@ -171,13 +174,16 @@ public function getBackendIconImageTag( public function convertValueToPath($varValue, $fallback) { if (Validator::isUuid($varValue)) { + /** @psalm-suppress InternalMethod - Class Adapter is internal, not the __call() method. Blame Contao. */ $model = $this->filesAdapter->findByPk($varValue); - if ($model !== null && file_exists($this->rootPath . '/' . $model->path)) { + if (($model instanceof FilesModel) && \file_exists($this->rootPath . '/' . $model->path)) { return $model->path; } + return $fallback; } - if (file_exists($varValue)) { + + if (\file_exists($varValue)) { return $varValue; } diff --git a/src/CoreBundle/Command/SchemaValidatorCommand.php b/src/CoreBundle/Command/SchemaValidatorCommand.php new file mode 100644 index 000000000..7a4583fe3 --- /dev/null +++ b/src/CoreBundle/Command/SchemaValidatorCommand.php @@ -0,0 +1,87 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\Command; + +use MetaModels\InformationProvider\MetaModelInformationCollector; +use MetaModels\Schema\SchemaGenerator; +use MetaModels\Schema\SchemaInformation; +use MetaModels\Schema\SchemaManager; +use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Input\InputInterface; +use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\OutputInterface; + +/** + * This validates the schema of the current installation. + */ +class SchemaValidatorCommand extends Command +{ + /** + * The information collector. + */ + private MetaModelInformationCollector $collector; + + /** + * The schema generator. + */ + private SchemaGenerator $generator; + + /** + * The schema manager. + */ + private SchemaManager $manager; + + public function __construct( + MetaModelInformationCollector $collector, + SchemaGenerator $generator, + SchemaManager $manager + ) { + $this->collector = $collector; + $this->generator = $generator; + $this->manager = $manager; + parent::__construct('metamodels:schema-update'); + } + + protected function configure(): void + { + parent::configure(); + $this->addOption('force', null, InputOption::VALUE_NONE, 'Perform the update'); + } + + protected function execute(InputInterface $input, OutputInterface $output): int + { + $this->generator->generate($information = new SchemaInformation(), $this->collector->getCollection()); + + if ($input->getOption('force')) { + $this->manager->preprocess($information); + $this->manager->process($information); + $this->manager->postprocess($information); + + return 0; + } + + foreach ($this->manager->validate($information) as $item) { + $output->writeln($item); + } + + return 0; + } +} diff --git a/src/CoreBundle/Contao/Compat/ContaoFactory.php b/src/CoreBundle/Contao/Compat/ContaoFactory.php index 4629839c4..0a50cb247 100644 --- a/src/CoreBundle/Contao/Compat/ContaoFactory.php +++ b/src/CoreBundle/Contao/Compat/ContaoFactory.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. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author binron * @author Stefan Heimes - * @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 */ @@ -36,13 +37,17 @@ class ContaoFactory * The Contao framework. * * @var ContaoFrameworkInterface + * + * @psalm-suppress DeprecatedInterface */ - private $framework; + private ContaoFrameworkInterface $framework; /** * Create a new instance. * * @param ContaoFrameworkInterface $framework The Contao framework. + * + * @psalm-suppress DeprecatedInterface */ public function __construct(ContaoFrameworkInterface $framework) { @@ -64,7 +69,7 @@ public function createInsertTags() /** * Create an adapter. * - * @param string $className The class name to create an adapter for. + * @param class-string $className The class name to create an adapter for. * * @return Adapter */ diff --git a/src/CoreBundle/Contao/Hooks/AbstractContentElementAndModuleCallback.php b/src/CoreBundle/Contao/Hooks/AbstractContentElementAndModuleCallback.php index 6d67df856..0ec480529 100644 --- a/src/CoreBundle/Contao/Hooks/AbstractContentElementAndModuleCallback.php +++ b/src/CoreBundle/Contao/Hooks/AbstractContentElementAndModuleCallback.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author Marc Reimann - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,14 +28,27 @@ use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; use ContaoCommunityAlliance\UrlBuilder\UrlBuilderFactoryInterface; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\BackendIntegration\TemplateList; use MetaModels\CoreBundle\Assets\IconBuilder; use MetaModels\Filter\Setting\FilterSettingFactory; use MetaModels\IFactory; +use RuntimeException; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function asort; +use function base64_decode; +use function base64_encode; +use function in_array; +use function reset; +use function sprintf; +use function trim; /** * This class is the abstract base for building the "edit MetaModel" button in the backend. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractContentElementAndModuleCallback { @@ -51,49 +64,51 @@ abstract class AbstractContentElementAndModuleCallback * * @var IconBuilder */ - private $iconBuilder; + private IconBuilder $iconBuilder; /** * The URL builder factory. * * @var UrlBuilderFactoryInterface */ - private $urlBuilderFactory; + private UrlBuilderFactoryInterface $urlBuilderFactory; /** * The MetaModel factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The filtersetting factory. * * @var FilterSettingFactory */ - private $filterFactory; + private FilterSettingFactory $filterFactory; /** * The database connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * The template list. * * @var TemplateList */ - private $templateList; + private TemplateList $templateList; /** * The request stack. * * @var RequestStack */ - private $requestStack; + private RequestStack $requestStack; + + private TranslatorInterface $translator; /** * Create a new instance. @@ -105,6 +120,7 @@ abstract class AbstractContentElementAndModuleCallback * @param Connection $connection The database connection. * @param TemplateList $templateList The template list loader. * @param RequestStack $requestStack The request stack. + * @param TranslatorInterface $translator The translator. */ public function __construct( IconBuilder $iconBuilder, @@ -113,7 +129,8 @@ public function __construct( FilterSettingFactory $filterFactory, Connection $connection, TemplateList $templateList, - RequestStack $requestStack + RequestStack $requestStack, + TranslatorInterface $translator, ) { $this->iconBuilder = $iconBuilder; $this->urlBuilderFactory = $urlBuilderFactory; @@ -122,6 +139,7 @@ public function __construct( $this->templateList = $templateList; $this->factory = $factory; $this->requestStack = $requestStack; + $this->translator = $translator; } /** @@ -140,14 +158,17 @@ public function editMetaModelButton(DC_Table $dataContainer) return ''; } - $url = $this->urlBuilderFactory->create('contao/main.php?do=metamodels&act=edit') + $url = $this->urlBuilderFactory->create('contao/metamodels?act=edit') ->setQueryParameter('id', ModelId::fromValues('tl_metamodel', $dataContainer->value)->getSerialized()); return $this->renderEditButton( - $GLOBALS['TL_LANG'][static::$tableName]['editmetamodel'][0], - sprintf( - StringUtil::specialchars($GLOBALS['TL_LANG'][static::$tableName]['editmetamodel'][1]), - $dataContainer->value + $this->translator->trans('editmetamodel.label', [], static::$tableName), + StringUtil::specialchars( + $this->translator->trans( + 'editmetamodel.description', + ['%id%' => $dataContainer->value], + static::$tableName + ) ), $url ); @@ -169,17 +190,20 @@ public function editFilterSettingButton(DC_Table $dataContainer) return ''; } - $url = $this->urlBuilderFactory->create('contao/main.php?do=metamodels&table=tl_metamodel_filtersetting') + $url = $this->urlBuilderFactory->create('contao/metamodels?table=tl_metamodel_filtersetting') ->setQueryParameter( 'pid', ModelId::fromValues('tl_metamodel_filter', $dataContainer->value)->getSerialized() ); return $this->renderEditButton( - $GLOBALS['TL_LANG'][static::$tableName]['editfiltersetting'][0], - sprintf( - StringUtil::specialchars($GLOBALS['TL_LANG'][static::$tableName]['editfiltersetting'][1]), - $dataContainer->value + $this->translator->trans('editfiltersetting.label', [], static::$tableName), + StringUtil::specialchars( + $this->translator->trans( + 'editfiltersetting.description', + ['%id%' => $dataContainer->value], + static::$tableName + ) ), $url ); @@ -201,17 +225,20 @@ public function editRenderSettingButton(DC_Table $dataContainer) return ''; } - $url = $this->urlBuilderFactory->create('contao/main.php?do=metamodels&table=tl_metamodel_rendersetting') + $url = $this->urlBuilderFactory->create('contao/metamodels?table=tl_metamodel_rendersetting') ->setQueryParameter( 'pid', ModelId::fromValues('tl_metamodel_rendersettings', $dataContainer->value)->getSerialized() ); return $this->renderEditButton( - $GLOBALS['TL_LANG'][static::$tableName]['editrendersetting'][0], - sprintf( - StringUtil::specialchars($GLOBALS['TL_LANG'][static::$tableName]['editrendersetting'][1]), - $dataContainer->value + $this->translator->trans('editrendersetting.label', [], static::$tableName), + StringUtil::specialchars( + $this->translator->trans( + 'editrendersetting.description', + ['%id%' => $dataContainer->value], + static::$tableName + ), ), $url ); @@ -230,12 +257,18 @@ public function editRenderSettingButton(DC_Table $dataContainer) public function getAttributeNames(DC_Table $objDc) { $attributeNames = [ - 'sorting' => $GLOBALS['TL_LANG']['MSC']['metamodels_sorting'], - 'random' => $GLOBALS['TL_LANG']['MSC']['random'], - 'id' => $GLOBALS['TL_LANG']['MSC']['id'][0] + 'sorting' => $this->translator->trans('metamodels_sorting', [], 'metamodels_list'), + 'random' => $this->translator->trans('random', [], 'metamodels_list'), + 'id' => $this->translator->trans('id', [], 'metamodels_list') ]; - $metaModelName = $this->factory->translateIdToMetaModelName($objDc->activeRecord->metamodel); + assert(null !== $objDc->activeRecord); + try { + $metaModelName = $this->factory->translateIdToMetaModelName($objDc->activeRecord->metamodel); + } catch (RuntimeException $exception) { + // No valid MetaModel selected, can not add attributes of it. + return $attributeNames; + } $metaModel = $this->factory->getMetaModel($metaModelName); if ($metaModel) { @@ -248,7 +281,7 @@ public function getAttributeNames(DC_Table $objDc) } /** - * Fetch all available filter settings for the current meta model. + * Fetch all available filter settings for the current MetaModel. * * @param DC_Table $objDC The data container calling this method. * @@ -256,13 +289,14 @@ public function getAttributeNames(DC_Table $objDc) */ public function getFilterSettings(DC_Table $objDC) { + assert(null !== $objDC->activeRecord); $filterSettings = $this->connection->createQueryBuilder() ->select('f.id', 'f.name') ->from('tl_metamodel_filter', 'f') ->where('f.pid=:id') ->setParameter('id', $objDC->activeRecord->metamodel) - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); $result = []; foreach ($filterSettings as $filterSetting) { @@ -287,6 +321,9 @@ public function getFilterSettings(DC_Table $objDC) */ public function getMetaTitleAttributes(DC_Table $objDC) { + assert(null !== $objDC->activeRecord); + + /** @psalm-suppress ArgumentTypeCoercion - We HOPE there is a list of strings. */ return $this->getFilteredAttributeNames( $objDC->activeRecord->metamodel, (array) $GLOBALS['METAMODELS']['metainformation']['allowedTitle'] @@ -305,6 +342,9 @@ public function getMetaTitleAttributes(DC_Table $objDC) */ public function getMetaDescriptionAttributes(DC_Table $objDC) { + assert(null !== $objDC->activeRecord); + + /** @psalm-suppress ArgumentTypeCoercion - We HOPE there is a list of strings. */ return $this->getFilteredAttributeNames( $objDC->activeRecord->metamodel, (array) $GLOBALS['METAMODELS']['metainformation']['allowedDescription'] @@ -319,16 +359,18 @@ public function getMetaDescriptionAttributes(DC_Table $objDC) * * @return void * + * @throws Exception * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ protected function buildFilterParamsFor(DC_Table $dataContainer, $elementName) { - if (!$this->requestStack->getCurrentRequest()->query->get('act')) { + $request = $this->requestStack->getCurrentRequest(); + if (null === $request || !$request->query->has('act')) { return; } - $filterId = $this->connection->createQueryBuilder() + $filterIds = $this->connection->createQueryBuilder() ->select('c.metamodel_filtering') ->from(static::$tableName, 'c') ->join('c', 'tl_metamodel', 'mm', 'mm.id=c.metamodel') @@ -337,10 +379,10 @@ protected function buildFilterParamsFor(DC_Table $dataContainer, $elementName) ->andWhere('c.type=:type') ->setParameter('type', $elementName) ->setMaxResults(1) - ->execute() - ->fetch(\PDO::FETCH_COLUMN); + ->executeQuery() + ->fetchFirstColumn(); - if (!$filterId) { + if (false === ($filterId = reset($filterIds)) || 0 === $filterId) { unset($GLOBALS['TL_DCA'][static::$tableName]['fields']['metamodel_filterparams']); return; } @@ -349,8 +391,11 @@ protected function buildFilterParamsFor(DC_Table $dataContainer, $elementName) $dca = $collection->getParameterDCA(); foreach ($dca as $fieldName => $subField) { $options = []; - foreach ($subField['options'] as $key => $value) { - $options[$this->loadCallback($key)] = $value; + foreach (($subField['options'] ?? []) as $key => $value) { + $newKey = $this->loadCallback($key); + if (null !== $newKey) { + $options[$newKey] = $value; + } } $dca[$fieldName]['options'] = $options; @@ -371,7 +416,7 @@ protected function buildFilterParamsFor(DC_Table $dataContainer, $elementName) */ public function saveCallback(string $value = null) { - return null === $value ? null : \base64_decode($value); + return null === $value ? null : base64_decode($value); } /** @@ -383,7 +428,7 @@ public function saveCallback(string $value = null) */ public function loadCallback(string $value = null) { - return null === $value ? null : trim(\base64_encode($value), '='); + return null === $value ? null : trim(base64_encode($value), '='); } /** @@ -395,16 +440,16 @@ public function loadCallback(string $value = null) */ public function getFilterParameterNames(DC_Table $objDc) { - $return = array(); + assert(null !== $objDc->activeRecord); + + $return = []; $filter = $objDc->activeRecord->metamodel_filtering; if (!$filter) { return $return; } - $collection = $this->filterFactory->createCollection($filter); - - return $collection->getParameterFilterNames(); + return $this->filterFactory->createCollection($filter)->getParameterFilterNames(); } /** @@ -416,6 +461,8 @@ public function getFilterParameterNames(DC_Table $objDc) */ public function getFilterTemplates(DC_Table $dcTable) { + assert(null !== $dcTable->activeRecord); + if ($dcTable->activeRecord->type === 'metamodels_frontendclearall') { return $this->templateList->getTemplatesForBase('mm_clearall_'); } @@ -436,7 +483,7 @@ protected function getTemplateList($base) } /** - * Fetch all available render settings for the current meta model. + * Fetch all available render settings for the current MetaModel. * * @param DC_Table $objDC The data container calling this method. * @@ -444,13 +491,15 @@ protected function getTemplateList($base) */ public function getRenderSettings(DC_Table $objDC) { + assert(null !== $objDC->activeRecord); + $filterSettings = $this->connection->createQueryBuilder() ->select('r.id', 'r.name') ->from('tl_metamodel_rendersettings', 'r') ->where('r.pid=:id') ->setParameter('id', $objDC->activeRecord->metamodel) - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); $result = []; foreach ($filterSettings as $filterSetting) { @@ -472,7 +521,7 @@ public function getRenderSettings(DC_Table $objDC) * * @return string */ - private function renderEditButton($caption, $title, UrlBuilder $url) + private function renderEditButton(string $caption, string $title, UrlBuilder $url): string { $icon = $this->iconBuilder->getBackendIconImageTag( 'system/themes/flexible/icons/alias.svg', @@ -493,19 +542,24 @@ private function renderEditButton($caption, $title, UrlBuilder $url) * * If the optional parameter arrTypes is not given, all attributes will be retrieved. * - * @param int $metaModelId The id of the MetaModel from which the attributes shall be retrieved from. - * - * @param string[] $allowedTypes The attribute type names that shall be retrieved (optional). + * @param string $metaModelId The id of the MetaModel from which the attributes shall be retrieved from. + * @param list $allowedTypes The attribute type names that shall be retrieved. * * @return array A list with all found attributes. */ - private function getFilteredAttributeNames($metaModelId, $allowedTypes = array()) + private function getFilteredAttributeNames(string $metaModelId, array $allowedTypes): array { - $attributeNames = array(); + $attributeNames = []; - if ($metaModel = $this->factory->getMetaModel($this->factory->translateIdToMetaModelName($metaModelId))) { + try { + $metaModelName = $this->factory->translateIdToMetaModelName($metaModelId); + } catch (RuntimeException $exception) { + // No valid MetaModel selected, can not add attributes of it. + return $attributeNames; + } + if ($metaModel = $this->factory->getMetaModel($metaModelName)) { foreach ($metaModel->getAttributes() as $attribute) { - if (empty($allowedTypes) || in_array($attribute->get('type'), $allowedTypes)) { + if (empty($allowedTypes) || in_array($attribute->get('type'), $allowedTypes, true)) { $attributeNames[$attribute->getColName()] = sprintf( '%s [%s]', diff --git a/src/CoreBundle/Contao/Hooks/ContentElementCallback.php b/src/CoreBundle/Contao/Hooks/ContentElementCallback.php index ede808094..f04697d1b 100644 --- a/src/CoreBundle/Contao/Hooks/ContentElementCallback.php +++ b/src/CoreBundle/Contao/Hooks/ContentElementCallback.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Marc Reimann * @author Stefan Heimes * @author Ingolf Steinhardt - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -60,9 +60,9 @@ public function buildFilterParameterList(DC_Table $dataContainer) */ public function getTemplates(DC_Table $objDC) { - /** @noinspection PhpUndefinedFieldInspection */ + assert(null !== $objDC->activeRecord); $type = $objDC->activeRecord->type; - if ($type == 'metamodel_content') { + if ($type === 'metamodel_content') { $type = 'metamodel_list'; } diff --git a/src/CoreBundle/Contao/Hooks/FixupUserGroupModules.php b/src/CoreBundle/Contao/Hooks/FixupUserGroupModules.php deleted file mode 100644 index 60256d1a4..000000000 --- a/src/CoreBundle/Contao/Hooks/FixupUserGroupModules.php +++ /dev/null @@ -1,148 +0,0 @@ - - * @author Ben - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace MetaModels\CoreBundle\Contao\Hooks; - -use Contao\DataContainer; -use MetaModels\ViewCombination\InputScreenInformationBuilder; -use MetaModels\ViewCombination\ViewCombinationBuilder; -use Symfony\Component\HttpFoundation\RequestStack; - -/** - * This is called as a HOOK from tl_user_group. - */ -class FixupUserGroupModules -{ - /** - * The view combination builder. - * - * @var ViewCombinationBuilder - */ - private $combinationBuilder; - - /** - * The input screen information builder. - * - * @var InputScreenInformationBuilder - */ - private $inputScreens; - - /** - * The request stack. - * - * @var RequestStack - */ - protected $requestStack; - - /** - * Create a new instance. - * - * @param ViewCombinationBuilder $combinationBuilder The view combination builder. - * @param InputScreenInformationBuilder $inputScreens The input screen information builder. - * @param RequestStack $requestStack The request stack. - */ - public function __construct( - ViewCombinationBuilder $combinationBuilder, - InputScreenInformationBuilder $inputScreens, - RequestStack $requestStack - ) { - $this->combinationBuilder = $combinationBuilder; - $this->inputScreens = $inputScreens; - $this->requestStack = $requestStack; - } - - /** - * Fix up the modules in the backend. - * - * @param DataContainer $dataContainer The current data container. - * - * @return array - * - * @throws \RuntimeException When the "parenting" class can not be found. - */ - public function fixupModules(DataContainer $dataContainer) - { - if (!class_exists('tl_user_group', false)) { - throw new \RuntimeException('data container is not loaded!'); - } - - $original = new \tl_user_group(); - $modules = $original->getModules($dataContainer); - - // 1. remove all MetaModels - foreach (array_keys($modules) as $group) { - foreach ($modules[$group] as $key => $module) { - if (strpos($module, 'metamodel_') === 0) { - unset($modules[$group][$key]); - } - } - // Otherwise we end up with an associative array. - $modules[$group] = array_values($modules[$group]); - } - - // 2. Add our "custom" modules and remove the main module. - $modules['metamodels'][] = 'support_metamodels'; - if (false !== $index = array_search('metamodels', $modules['metamodels'], true)) { - unset($modules['metamodels'][$index]); - $modules['metamodels'] = array_values($modules['metamodels']); - } - - // 3. Add back all MetaModels for the current group. - $combinations = $this->combinationBuilder->getCombinationsForUser([$dataContainer->activeRecord->id], 'be'); - - $screenIds = array_map(function ($combination) { - return $combination['dca_id']; - }, $combinations['byName']); - - $screens = $this->inputScreens->fetchInputScreens($screenIds); - - $locale = $this->requestStack->getCurrentRequest()->getLocale(); - foreach ($screens as $metaModel => $screen) { - if ('standalone' === $screen['meta']['rendertype']) { - $modules[$screen['meta']['backendsection']][] = 'metamodel_' . $metaModel; - $this->buildLanguageString('metamodel_' . $metaModel, $screen, $locale); - } - } - - return $modules; - } - - /** - * Build the language string for the passed backend module. - * - * @param string $name The module name. - * @param array $screen The input screen information. - * @param string $locale The locale. - * - * @return void - * - * @SuppressWarnings(PHPMD.Superglobals) - * @SuppressWarnings(PHPMD.CamelCaseVariableName) - */ - private function buildLanguageString($name, $screen, $locale) - { - if (isset($screen['label'][$locale])) { - $GLOBALS['TL_LANG']['MOD'][$name] = $screen['label'][$locale]; - return; - } - - $GLOBALS['TL_LANG']['MOD'][$name] = $screen['label']['']; - } -} diff --git a/src/CoreBundle/Contao/Hooks/LoadDataContainer.php b/src/CoreBundle/Contao/Hooks/LoadDataContainer.php index bb6512379..08ce3af38 100644 --- a/src/CoreBundle/Contao/Hooks/LoadDataContainer.php +++ b/src/CoreBundle/Contao/Hooks/LoadDataContainer.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,8 +23,12 @@ use Contao\Controller; use Contao\CoreBundle\Framework\Adapter; +use Contao\StringUtil; +use Contao\System; +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use MetaModels\CoreBundle\Assets\IconBuilder; +use MetaModels\Helper\LocaleUtil; use MetaModels\IFactory; use MetaModels\IMetaModel; use MetaModels\ViewCombination\ViewCombination; @@ -37,30 +41,30 @@ class LoadDataContainer /** * Adapter to the Contao\Controller class. * - * @var Controller + * @var Adapter */ - private $controller; + private Adapter $controller; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The view combination. * * @var ViewCombination */ - private $combination; + private ViewCombination $combination; /** * The icon builder. * * @var IconBuilder */ - private $iconBuilder; + private IconBuilder $iconBuilder; /** * Create a new instance. @@ -89,22 +93,20 @@ public function __construct( * * @return void */ - public function onLoadDataContainer($tableName) + public function onLoadDataContainer($tableName): void { - // @codingStandardsIgnoreStart - // FIXME: make this beautiful. - // @codingStandardsIgnoreEnd - if (!\System::getContainer()->get('cca.dc-general.scope-matcher')->currentScopeIsBackend()) { + $scopeMatcher = System::getContainer()->get('cca.dc-general.scope-matcher'); + if (!($scopeMatcher instanceof RequestScopeDeterminator) || !$scopeMatcher->currentScopeIsBackend()) { return; } static $tableExists; // Test that the tables have been created. if (null === $tableExists) { - $tableExists = \System::getContainer() - ->get('database_connection') - ->getSchemaManager() - ->tablesExist(['tl_metamodel']); + if (null === ($connection = System::getContainer()->get('database_connection'))) { + return; + } + $tableExists = $connection->createSchemaManager()->tablesExist(['tl_metamodel']); } if (false === $tableExists) { return; @@ -124,14 +126,14 @@ public function onLoadDataContainer($tableName) * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - private function handleMetaModelTable($tableName) + private function handleMetaModelTable(string $tableName): void { static $tableNames; if (!$tableNames) { $tableNames = $this->factory->collectNames(); } // Not a MetaModel, get out now. - if (!in_array($tableName, $tableNames)) { + if (!\in_array($tableName, $tableNames)) { return; } @@ -141,7 +143,7 @@ private function handleMetaModelTable($tableName) $GLOBALS['TL_DCA'][$tableName] = []; } - $GLOBALS['TL_DCA'][$tableName] = array_replace_recursive( + $GLOBALS['TL_DCA'][$tableName] = \array_replace_recursive( (array) $GLOBALS['TL_DCA']['tl_metamodel_item'], (array) $GLOBALS['TL_DCA'][$tableName] ); @@ -157,10 +159,10 @@ private function handleMetaModelTable($tableName) * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - private function handleNonMetaModelTable($tableName) + private function handleNonMetaModelTable(string $tableName): void { // Nothing to do for MetaModel tables. - if (substr($tableName, 0, 3) === 'mm_') { + if (\str_starts_with($tableName, 'mm_')) { return; } @@ -179,16 +181,17 @@ private function handleNonMetaModelTable($tableName) $this->controller->loadLanguageFile('default'); foreach ($map[$tableName] as $metaModelTable => $inputScreen) { $metaModel = $this->factory->getMetaModel($metaModelTable); - $caption = $this->buildCaption($metaModel, $inputScreen); + assert($metaModel instanceof IMetaModel); + + $caption = $this->buildCaption($metaModel, $inputScreen); $operationName = 'edit_' . $metaModel->getTableName(); - $parentDCA['list']['operations'][$operationName] = array - ( + $parentDCA['list']['operations'][$operationName] = [ 'label' => &$caption, 'href' => 'table=' . $metaModelTable, 'icon' => $this->iconBuilder->getBackendIcon($inputScreen['meta']['backendicon']), 'attributes' => 'onclick="Backend.getScrollOffset()"', - ); + ]; // Is the destination table a metamodel with variants? if ($metaModel->hasVariants()) { @@ -202,7 +205,15 @@ private function handleNonMetaModelTable($tableName) $idParameter = $parentDCA['list']['operations'][$operationName]['idparam']; $parentDCA['list']['operations'][$operationName]['button_callback'] = - function ($row, $href, $label, $name, $icon, $attributes, $table) use ($idParameter) { + function ( + array $row, + string $href, + string $label, + string $name, + string $icon, + string $attributes, + string $table + ) use ($idParameter): string { return $this->buildChildOperationButton( $idParameter, $row['id'], @@ -223,7 +234,7 @@ function ($row, $href, $label, $name, $icon, $attributes, $table) use ($idParame * * @return array */ - private function buildMap() + private function buildMap(): array { $map = []; foreach ($this->combination->getParented() as $childName => $child) { @@ -244,14 +255,15 @@ private function buildMap() * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - private function buildCaption($metaModel, $inputScreen): array + private function buildCaption(IMetaModel $metaModel, array $inputScreen): array { $caption = [ - sprintf($GLOBALS['TL_LANG']['MSC']['metamodel_edit_as_child']['label'], $metaModel->getName()), + \sprintf($GLOBALS['TL_LANG']['MSC']['metamodel_edit_as_child']['label'], $metaModel->getName()), '' ]; - $currentLanguage = \str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + $currentLanguage = LocaleUtil::formatAsLocale($GLOBALS['TL_LANGUAGE']); foreach ($inputScreen['label'] as $langCode => $label) { if ($label !== '' && $langCode === $currentLanguage) { $caption = [ @@ -268,25 +280,26 @@ private function buildCaption($metaModel, $inputScreen): array * This method exists only for being compatible when MetaModels are being used as child table from DC_Table context. * * @param string $idParameter The id parameter in use. - * - * @param string $itemId The current data row. - * - * @param string $href The href to be appended. - * - * @param string $label The operation label. - * - * @param string $name The operation name. - * - * @param string $icon The icon path. - * - * @param string $attributes The button attributes. - * - * @param string $table The table name. + * @param string $itemId The current data row. + * @param string $href The href to be appended. + * @param string $label The operation label. + * @param string $name The operation name. + * @param string $icon The icon path. + * @param string $attributes The button attributes. + * @param string $table The table name. * * @return string */ - private function buildChildOperationButton($idParameter, $itemId, $href, $label, $name, $icon, $attributes, $table) - { + private function buildChildOperationButton( + string $idParameter, + string $itemId, + string $href, + string $label, + string $name, + string $icon, + string $attributes, + string $table + ): string { $modelId = ModelId::fromValues($table, $itemId); $url = $href . '&' . $idParameter . '=' . $modelId->getSerialized(); @@ -297,14 +310,15 @@ private function buildChildOperationButton($idParameter, $itemId, $href, $label, $url = $this->controller->addToUrl($url); // If id parameter different, we have to clean out the id in the URL now. if ('id' !== $idParameter) { - $url = preg_replace('#(&)id=(?:&)?#', '$1', $url); + $url = \preg_replace('#(&)id=(?:&)?#', '$1', $url); } - $title = sprintf($label ?: $name, $itemId); - return sprintf( + $title = \sprintf($label ?: $name, $itemId); + + return \sprintf( '%4$s ', $url, - specialchars($title), + StringUtil::specialchars($title), $attributes, $this->iconBuilder->getBackendIconImageTag($icon, $label) ); diff --git a/src/CoreBundle/Contao/Hooks/ModuleCallback.php b/src/CoreBundle/Contao/Hooks/ModuleCallback.php index cfb5c1644..7879dc3d6 100644 --- a/src/CoreBundle/Contao/Hooks/ModuleCallback.php +++ b/src/CoreBundle/Contao/Hooks/ModuleCallback.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,13 +13,15 @@ * @package MetaModels/core * @author Christian Schiffler * @author Ingolf Steinhardt - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\Contao\Hooks; +use Contao\DC_Table; + /** * This class provides callbacks for tl_module. */ @@ -35,11 +37,11 @@ class ModuleCallback extends AbstractContentElementAndModuleCallback /** * Called from tl_content.onload_callback. * - * @param \DC_Table $dataContainer The data container calling this method. + * @param DC_Table $dataContainer The data container calling this method. * * @return void */ - public function buildFilterParameterList(\DC_Table $dataContainer) + public function buildFilterParameterList(DC_Table $dataContainer) { parent::buildFilterParamsFor($dataContainer, 'metamodel_list'); } @@ -47,12 +49,13 @@ public function buildFilterParameterList(\DC_Table $dataContainer) /** * Fetch the template group for the current MetaModel frontend module. * - * @param \DC_Table $objDC The data container calling this method. + * @param DC_Table $objDC The data container calling this method. * * @return array */ - public function getTemplates(\DC_Table $objDC) + public function getTemplates(DC_Table $objDC) { + assert(null !== $objDC->activeRecord); $type = $objDC->activeRecord->type; return $this->getTemplateList('mod_' . $type); diff --git a/src/CoreBundle/Contao/InsertTag/ReplaceParam.php b/src/CoreBundle/Contao/InsertTag/ReplaceParam.php index 8e6f7c31c..2b0d67068 100644 --- a/src/CoreBundle/Contao/InsertTag/ReplaceParam.php +++ b/src/CoreBundle/Contao/InsertTag/ReplaceParam.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Oliver Hoff * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,8 +27,10 @@ namespace MetaModels\CoreBundle\Contao\InsertTag; use Contao\CoreBundle\Framework\Adapter; +use Contao\CoreBundle\Framework\ContaoFramework; use Contao\Input; -use Contao\Session; +use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; /** * This replaces the insert tag param. @@ -36,29 +38,36 @@ final class ReplaceParam { /** - * The input. + * The input framework. * - * @var Input + * @var ContaoFramework */ - private $input; + private ContaoFramework $framework; /** - * The session. + * The adapter. * - * @var Session + * @var Adapter|null */ - private $session; + private ?Adapter $input = null; + + /** + * The request stack. + * + * @var RequestStack + */ + private RequestStack $requestStack; /** * ReplaceParam constructor. * - * @param Adapter $input The input. - * @param Session $session The session. + * @param ContaoFramework $framework The input framework. + * @param RequestStack $requestStack The session. */ - public function __construct(Adapter $input, Session $session) + public function __construct(ContaoFramework $framework, RequestStack $requestStack) { - $this->input = $input; - $this->session = $session; + $this->framework = $framework; + $this->requestStack = $requestStack; } /** @@ -71,7 +80,9 @@ public function __construct(Adapter $input, Session $session) */ public function replace(string $content): ?string { - if (false === \strpos($content, '{{') + $tags = []; + if ( + !\str_contains($content, '{{') || !($tags = preg_split('@\{\{(.*)\}\}@', $content, -1, PREG_SPLIT_DELIM_CAPTURE)) || (\count($tags) < 2) ) { @@ -80,7 +91,8 @@ public function replace(string $content): ?string $newContent = null; foreach ($tags as $tag) { - if (!(2 === \count($chunks = \explode('::', $tag, 2))) + if ( + !(2 === \count($chunks = \explode('::', $tag, 2))) || !('param' === $chunks[0]) || !($this->isParameterSupported($chunks[1], ['get', 'post', 'cookie', 'session', 'filter'])) ) { @@ -96,28 +108,33 @@ public function replace(string $content): ?string /** * Replace the insert tag with the input value. * - * @param array $chunks The chunks. - * @param string|null $content The content. - * @param string $tag The tag. + * @param list $chunks The chunks. + * @param string|null $content The content. + * @param string $tag The tag. * * @return string|null + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function replaceInputParameter(array $chunks, ?string $content, string $tag): ?string { - if ((null === $content) - || !($this->isParameterSupported($chunks[1], ['get', 'post', 'cookie'])) - || !($arguments = $this->splitParameter($chunks[1])) - ) { + if (null === ($arguments = $this->getArguments($chunks[1], $content, ['get', 'post', 'cookie']))) { return $content; } + assert(\is_string($content)); - if ((false === \strpos($tag, '&default='))) { + if (null === $this->input) { + /** @psalm-suppress InternalMethod - Class ContaoFramework is internal, not the getAdapter() method. */ + $this->input = $this->framework->getAdapter(Input::class); + } + + if ((!\str_contains($tag, '&default='))) { if (null === ($result = $this->input->{$arguments[0]}($arguments[1]))) { return null; } return \str_replace( '{{' . $tag . '}}', - \is_array($result) ? \serialize($result) : $result, + \is_array($result) ? \serialize($result) : ($result ?? ''), $content ); } @@ -125,7 +142,7 @@ private function replaceInputParameter(array $chunks, ?string $content, string $ $result = ($this->input->{$arguments[0]}($arguments[1]) ?: $arguments[2]); return \str_replace( '{{' . $tag . '}}', - \is_array($result) ? \serialize($result) : $result, + \is_array($result) ? \serialize($result) : ($result ?? ''), $content ); } @@ -133,23 +150,24 @@ private function replaceInputParameter(array $chunks, ?string $content, string $ /** * Replace the insert tag with the session value. * - * @param array $chunks The chunks. - * @param string|null $content The content. - * @param string $tag The tag. + * @param list $chunks The chunks. + * @param string|null $content The content. + * @param string $tag The tag. * * @return string|null */ private function replaceSessionParameter(array $chunks, ?string $content, string $tag): ?string { - if ((null === $content) - || !($this->isParameterSupported($chunks[1], ['session'])) - || !($arguments = $this->splitParameter($chunks[1])) - ) { + if (null === ($arguments = $this->getArguments($chunks[1], $content, ['session']))) { return $content; } + assert(\is_string($content)); - if ((false === \strpos($tag, '&default='))) { - $result = $this->session->get($arguments[1]); + $sessionBag = $this->requestStack->getSession()->getBag('contao_frontend'); + assert($sessionBag instanceof AttributeBagInterface); + + if ((!\str_contains($tag, '&default='))) { + $result = $sessionBag->get($arguments[1]); return \str_replace( '{{' . $tag . '}}', \is_array($result) ? \serialize($result) : (string) $result, @@ -157,7 +175,7 @@ private function replaceSessionParameter(array $chunks, ?string $content, string ); } - $result = ($this->session->get($arguments[1]) ?: $arguments[2]); + $result = ($sessionBag->get($arguments[1]) ?: $arguments[2]); return \str_replace( '{{' . $tag . '}}', \is_array($result) ? \serialize($result) : (string) $result, @@ -165,22 +183,41 @@ private function replaceSessionParameter(array $chunks, ?string $content, string ); } + /** + * @param list $supported + * + * @return list|null + */ + private function getArguments(string $chunk, ?string $content, array $supported): ?array + { + if ((null === $content) || !$this->isParameterSupported($chunk, $supported)) { + return null; + } + $arguments = $this->splitParameter($chunk); + if ((null === $arguments) || ([] === $arguments)) { + return null; + } + + return $arguments; + } + /** * Split the parameter. * * @param string $parameter The parameter. * - * @return array|null + * @return list|null */ private function splitParameter(string $parameter): ?array { - if ((2 !== \count($chunks = \explode('?', $parameter))) - || (0 !== \strpos($chunks[1], 'name=')) + if ( + (2 !== \count($chunks = \explode('?', $parameter))) + || (!\str_starts_with($chunks[1], 'name=')) ) { return null; } - if (false === \strpos($chunks[1], '&default=')) { + if (!\str_contains($chunks[1], '&default=')) { return [$chunks[0], \substr($chunks[1], \strlen('name='))]; } @@ -203,7 +240,7 @@ private function isParameterSupported(string $parameter, array $supported): bool { $isSupported = false; foreach ($supported as $name) { - if (0 !== \strpos($parameter, $name)) { + if (!\str_starts_with($parameter, $name)) { continue; } diff --git a/src/CoreBundle/Contao/InsertTag/ResolveLanguageTag.php b/src/CoreBundle/Contao/InsertTag/ResolveLanguageTag.php index 47d092b32..d1f6e7377 100644 --- a/src/CoreBundle/Contao/InsertTag/ResolveLanguageTag.php +++ b/src/CoreBundle/Contao/InsertTag/ResolveLanguageTag.php @@ -13,6 +13,7 @@ * @package MetaModels/core * @author Christian Schiffler * @author Oliver Willmes + * @author Ingolf Steinhardt * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource @@ -60,14 +61,15 @@ public function __construct(RequestStack $requestStack) public function resolve(string $queryString): string { // @codingStandardsIgnoreStart - if (\strpos($queryString, '{{iflng') === false && \strpos($queryString, '{{ifnlng') === false) { + if (!\str_contains($queryString, '{{iflng') && !\str_contains($queryString, '{{ifnlng')) { return $queryString; } $tags = \preg_split('~{{(ifn?lng[^{}]*)}}~', $queryString, -1, PREG_SPLIT_DELIM_CAPTURE ); $strBuffer = ''; + $arrCache = []; - for ($_rit=0, $_cnt=\count($tags); $_rit<$_cnt; $_rit+=2) { + for ($_rit = 0, $_cnt = \count($tags); $_rit < $_cnt; $_rit += 2) { $strBuffer .= $tags[$_rit]; if (!isset($tags[$_rit+1])) { @@ -81,12 +83,12 @@ public function resolve(string $queryString): string $flags = \explode('|', $strTag); $tag = \array_shift($flags); - $elements = \explode('::', $tag); + $elements = \array_merge(\explode('::', $tag), ['']); $arrCache[$strTag] = ''; if ( - !empty($elements[1]) && + '' !== $elements[1] && $this->languageMatches($elements[1]) === (\strtolower($elements[0]) === 'ifnlng') ) { for (; $_rit<$_cnt; $_rit+=2) { diff --git a/src/CoreBundle/ContaoManager/Plugin.php b/src/CoreBundle/ContaoManager/Plugin.php index 5545faa07..0d882b31c 100644 --- a/src/CoreBundle/ContaoManager/Plugin.php +++ b/src/CoreBundle/ContaoManager/Plugin.php @@ -59,8 +59,10 @@ public function getBundles(ParserInterface $parser) */ public function getRouteCollection(LoaderResolverInterface $resolver, KernelInterface $kernel) { - return $resolver - ->resolve(__DIR__.'/../Resources/config/routing.yml') - ->load(__DIR__.'/../Resources/config/routing.yml'); + if (false === ($resolved = $resolver->resolve(__DIR__ . '/../Resources/config/routing.yml'))) { + return null; + } + + return $resolved->load(__DIR__ . '/../Resources/config/routing.yml'); } } diff --git a/src/CoreBundle/Controller/Backend/AbstractAddAllController.php b/src/CoreBundle/Controller/Backend/AbstractAddAllController.php index 3000399d5..1d5981599 100644 --- a/src/CoreBundle/Controller/Backend/AbstractAddAllController.php +++ b/src/CoreBundle/Controller/Backend/AbstractAddAllController.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,55 +32,59 @@ use Symfony\Component\HttpFoundation\RedirectResponse; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use Twig\Environment as TwigEnvironment; /** * This controller provides the base for the add-all handlers for input screens and render settings. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @psalm-suppress PropertyNotSetInConstructor */ abstract class AbstractAddAllController { /** * Adapter to the Contao\System class. * - * @var System + * @var Adapter */ - private $systemAdapter; + private Adapter $systemAdapter; /** * The translator. * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The database connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * The cache purger. * * @var PurgeCache */ - private $purger; + private PurgeCache $purger; /** * The list of known attributes. * * @var array */ - private $knownAttributes; + private array $knownAttributes; /** * The twig engine. @@ -94,7 +98,7 @@ abstract class AbstractAddAllController * * @var int */ - private $startSort; + private int $startSort; /** * Create a new instance. @@ -103,7 +107,7 @@ abstract class AbstractAddAllController * @param TranslatorInterface $translator The translator. * @param IFactory $factory The MetaModels factory. * @param Connection $connection The database connection. - * @param Adapter $systemAdapter Adapter to the Contao\System class. + * @param Adapter $systemAdapter Adapter to the Contao\System class. * @param PurgeCache $purger The cache purger. */ public function __construct( @@ -129,10 +133,11 @@ public function __construct( * @param string $parentId The parent id. * @param bool $activate Flag if the setting shall get activated. * @param int $sort The sorting value. + * @param string $tlclass The CSS class. * * @return array */ - abstract protected function createEmptyDataFor(IAttribute $attribute, $parentId, $activate, $sort); + abstract protected function createEmptyDataFor(IAttribute $attribute, $parentId, $activate, $sort, $tlclass = ''); /** * Test if the passed attribute is acceptable. @@ -194,18 +199,18 @@ protected function render($table, $metaModel, Request $request) return [ 'action' => '', - 'requestToken' => REQUEST_TOKEN, + 'requestToken' => System::getContainer()->get('contao.csrf.token_manager')?->getDefaultTokenValue(), 'href' => $this->getReferer($request, $table, true), - 'backBt' => $this->translator->trans('MSC.backBT', [], 'contao_default'), - 'add' => $this->translator->trans('MSC.continue', [], 'contao_default'), - 'saveNclose' => $this->translator->trans('MSC.saveNclose', [], 'contao_default'), - 'activate' => $this->translator->trans($table . '.addAll_activate', [], 'contao_' . $table), + 'backBt' => $this->translator->trans('backBT', [], $table), + 'add' => $this->translator->trans('continue', [], $table), + 'saveNclose' => $this->translator->trans('saveNclose', [], $table), + 'activate' => $this->translator->trans('addAll_activate', [], $table), 'tlclass' => '', - 'headline' => $this->translator->trans($table . '.addall.1', [], 'contao_' . $table), - 'selectAll' => $this->translator->trans('MSC.selectAll', [], 'contao_default') . '.', + 'headline' => $this->translator->trans('addall.description', [], $table), + 'selectAll' => $this->translator->trans('selectAll', [], $table) . '.', 'cacheMessage' => '', 'updateMessage' => '', - 'hasCheckbox' => count($fields) > 0, + 'hasCheckbox' => \count($fields) > 0, 'fields' => $fields, 'stylesheets' => ['bundles/metamodelscore/css/style.css'] ]; @@ -219,7 +224,7 @@ protected function render($table, $metaModel, Request $request) * * @return array */ - private function fetchExisting($table, $parentId) + private function fetchExisting(string $table, string $parentId): array { // Keep the sorting value. $this->startSort = 0; @@ -232,9 +237,9 @@ private function fetchExisting($table, $parentId) ->where('t.pid=:pid') ->setParameter('pid', $parentId) ->orderBy('t.sorting') - ->execute(); + ->executeQuery(); - foreach ($alreadyExisting->fetchAll(\PDO::FETCH_ASSOC) as $item) { + foreach ($alreadyExisting->fetchAllAssociative() as $item) { $this->knownAttributes[$item['attr_id']] = $item; $this->startSort = $item['sorting']; } @@ -249,9 +254,9 @@ private function fetchExisting($table, $parentId) * * @return bool */ - private function knowsAttribute($attribute) + private function knowsAttribute(IAttribute $attribute): bool { - return array_key_exists($attribute->get('id'), $this->knownAttributes); + return \array_key_exists($attribute->get('id'), $this->knownAttributes); } /** @@ -263,7 +268,7 @@ private function knowsAttribute($attribute) * * @return array */ - private function generateForm($table, $metaModel, Request $request) + private function generateForm(string $table, IMetaModel $metaModel, Request $request): array { $fields = []; // Loop over all attributes now. @@ -312,12 +317,16 @@ private function generateForm($table, $metaModel, Request $request) * * @return string */ - private function checkboxCaption($key, $table, IAttribute $attribute) + private function checkboxCaption(string $key, string $table, IAttribute $attribute): string { return $this->translator->trans( - $table . '.' . $key, - [$attribute->getName(), $attribute->get('type'), $attribute->getColName()], - 'contao_' . $table + $key, + [ + '%name%' => $attribute->getName(), + '%type%' => $attribute->get('type'), + '%colName%' => $attribute->getColName() + ], + $table ); } @@ -329,7 +338,7 @@ private function checkboxCaption($key, $table, IAttribute $attribute) * * @return bool */ - private function isAttributeSubmitted($attributeId, Request $request) + private function isAttributeSubmitted(string $attributeId, Request $request): bool { return $request->request->has('attribute_' . $attributeId); } @@ -344,37 +353,41 @@ private function isAttributeSubmitted($attributeId, Request $request) * * @return void */ - private function perform($table, Request $request, $metaModel, $parentId) + private function perform(string $table, Request $request, IMetaModel $metaModel, string $parentId): void { $activate = (bool) $request->request->get('activate'); - $tlclass = $request->request->get('tlclass'); + $tlclass = (string) $request->request->get('tlclass'); $query = $this ->connection ->createQueryBuilder() ->insert($table); foreach ($metaModel->getAttributes() as $attribute) { - if ($this->knowsAttribute($attribute) + if ( + $this->knowsAttribute($attribute) || !($this->accepts($attribute) && $this->isAttributeSubmitted($attribute->get('id'), $request)) ) { continue; } $data = []; - foreach ($this->createEmptyDataFor( - $attribute, - $parentId, - $activate, - $this->startSort, - $tlclass - ) as $key => $value) { + foreach ( + $this->createEmptyDataFor( + $attribute, + $parentId, + $activate, + $this->startSort, + $tlclass + ) as $key => $value + ) { $data[$key] = ':' . $key; $query->setParameter($key, $value); } - $query->values($data)->execute(); + $query->values($data)->executeQuery(); $this->startSort += 128; } + $this->purger->purge(); } @@ -387,7 +400,7 @@ private function perform($table, Request $request, $metaModel, $parentId) * * @return string */ - private function getReferer(Request $request, $table, $encodeAmp = false) + private function getReferer(Request $request, string $table, bool $encodeAmp = false): string { $uri = $this->systemAdapter->getReferer($encodeAmp, $table); // Make the location an absolute URL diff --git a/src/CoreBundle/Controller/Backend/ConfigurationController.php b/src/CoreBundle/Controller/Backend/ConfigurationController.php new file mode 100644 index 000000000..6795b63cb --- /dev/null +++ b/src/CoreBundle/Controller/Backend/ConfigurationController.php @@ -0,0 +1,96 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\Controller\Backend; + +use Contao\CoreBundle\Framework\ContaoFramework; +use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactoryService; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Twig\Environment as TwigEnvironment; +use Twig\Error\LoaderError; +use Twig\Error\RuntimeError; +use Twig\Error\SyntaxError; + +final class ConfigurationController +{ + use DcGeneralControllerTrait; + + /** + * @param Request $request The request. + * @param TwigEnvironment $twig The twig environment. + * @param DcGeneralFactoryService $factoryFactory The DCG factory + * @param EventDispatcherInterface $dispatcher The event dispatcher. + * @param TranslatorInterface $translator The translator. + * @param ContaoFramework $framework The Contao framework + * + * @return Response + * @throws LoaderError + * @throws RuntimeError + * @throws SyntaxError + */ + public function __invoke( + Request $request, + TwigEnvironment $twig, + DcGeneralFactoryService $factoryFactory, + EventDispatcherInterface $dispatcher, + TranslatorInterface $translator, + ContaoFramework $framework, + ): Response { + $containerName = (string) $request->query->get('table', 'tl_metamodel'); + $controllerResult = $this->bootDcGeneralAndProcess( + $request, + $containerName, + $factoryFactory, + $dispatcher, + $translator, + $framework + ); + $headline = $this->determineHeadline($containerName, $translator); + + return new Response( + $twig->render( + '@MetaModelsCore/Backend/be_config.html.twig', + [ + 'headline' => $headline, + 'body' => $controllerResult, + 'stylesheets' => ['bundles/metamodelscore/css/style.css'] + ] + ) + ); + } + + /** + * Generate headline. + * + * @param string $containerName The container. + * @param TranslatorInterface $translator The translator. + * + * @return string + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + */ + private function determineHeadline(string $containerName, TranslatorInterface $translator): string + { + return $translator->translate('backend-module.headline', $containerName); + } +} diff --git a/src/CoreBundle/Controller/Backend/DcGeneralControllerTrait.php b/src/CoreBundle/Controller/Backend/DcGeneralControllerTrait.php new file mode 100644 index 000000000..5651e4b2c --- /dev/null +++ b/src/CoreBundle/Controller/Backend/DcGeneralControllerTrait.php @@ -0,0 +1,94 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\Controller\Backend; + +use Contao\Ajax; +use Contao\Controller; +use Contao\CoreBundle\Framework\ContaoFramework; +use ContaoCommunityAlliance\DcGeneral\Action; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; +use ContaoCommunityAlliance\DcGeneral\Controller\ControllerInterface; +use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactoryService; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; + +trait DcGeneralControllerTrait +{ + /** + * @param Request $request The request. + * @param string $tableName The table name + * @param DcGeneralFactoryService $factoryFactory The DCG factory + * @param EventDispatcherInterface $dispatcher The event dispatcher. + * @param TranslatorInterface $translator The translator. + * @param ContaoFramework $framework The Contao framework + * + * @return string + * @throws \Exception + */ + public function bootDcGeneralAndProcess( + Request $request, + string $tableName, + DcGeneralFactoryService $factoryFactory, + EventDispatcherInterface $dispatcher, + TranslatorInterface $translator, + ContaoFramework $framework, + ): string { + $act = (string) $request->query->get('act', 'showAll'); + + // Work around legacy Contao code. + /** @psalm-suppress InternalMethod - Class ContaoFramework is internal, not the getAdapter() method. */ + $contaoController = $framework->getAdapter(Controller::class); + // Need to load the language file due to Widget class using hardcoded lang array offsets. + $contaoController->loadLanguageFile('default'); + + // Handle Ajax calls. + $action = null; + if ($request->isXmlHttpRequest() && '' !== ($action = (string) $request->request->get('action', ''))) { + $ajaxClass = new Ajax($action); + $ajaxClass->executePreActions(); + } + + // Build data container. + $factory = $factoryFactory->createFactory(); + $general = $factory + ->setContainerName($tableName) + ->setTranslator($translator) + ->setEventDispatcher($dispatcher) + ->createDcGeneral(); + + $environment = $general->getEnvironment(); + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + + // Load the clipboard. + $clipboard->loadFrom($environment); + + $controller = $environment->getController(); + assert($controller instanceof ControllerInterface); + + if (null !== $action) { + $environment->getView()?->handleAjaxCall(); + } + + return $controller->handle(new Action($act)); + } +} diff --git a/src/CoreBundle/Controller/Backend/InputScreenAddAllController.php b/src/CoreBundle/Controller/Backend/InputScreenAddAllController.php index 57185a847..4106bd209 100644 --- a/src/CoreBundle/Controller/Backend/InputScreenAddAllController.php +++ b/src/CoreBundle/Controller/Backend/InputScreenAddAllController.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,7 +30,7 @@ use MetaModels\IMetaModel; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use Twig\Environment as TwigEnvironment; /** @@ -43,7 +43,7 @@ class InputScreenAddAllController extends AbstractAddAllController * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * The twig engine. @@ -102,12 +102,20 @@ protected function render($table, $metaModel, Request $request) { return \array_merge( parent::render($table, $metaModel, $request), - ['tlclass' => $this->translator->trans($table . '.addAll_tlclass', [], 'contao_' . $table)] + ['tlclass' => $this->translator->trans('addAll_tlclass', [], $table)] ); } /** - * {@inheritDoc} + * Create an empty data set for inclusion into the database. + * + * @param IAttribute $attribute The attribute to generate the data for. + * @param string $parentId The parent id. + * @param bool $activate Flag if the setting shall get activated. + * @param int $sort The sorting value. + * @param string $tlclass The CSS class. + * + * @return array * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ @@ -119,7 +127,7 @@ protected function createEmptyDataFor(IAttribute $attribute, $parentId, $activat 'attr_id' => $attribute->get('id'), 'pid' => $parentId, 'sorting' => $sort, - 'tstamp' => time(), + 'tstamp' => \time(), 'published' => $activate ? '1' : '' ]; } diff --git a/src/CoreBundle/Controller/Backend/MetaModelController.php b/src/CoreBundle/Controller/Backend/MetaModelController.php new file mode 100644 index 000000000..7dfe28bc6 --- /dev/null +++ b/src/CoreBundle/Controller/Backend/MetaModelController.php @@ -0,0 +1,114 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\Controller\Backend; + +use Contao\CoreBundle\Exception\AccessDeniedException; +use Contao\CoreBundle\Framework\ContaoFramework; +use Contao\CoreBundle\Menu\BackendMenuBuilder; +use ContaoCommunityAlliance\DcGeneral\Factory\DcGeneralFactoryService; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use MetaModels\ViewCombination\ViewCombination; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Component\HttpFoundation\Response; +use Twig\Environment as TwigEnvironment; + +final class MetaModelController +{ + use DcGeneralControllerTrait; + + /** + * The constructor. + * + * @param BackendMenuBuilder $builder The menu builder. + */ + public function __construct( + private readonly BackendMenuBuilder $builder + ) { + } + + /** + * @param Request $request The request. + * @param TwigEnvironment $twig The twig environment. + * @param DcGeneralFactoryService $factoryFactory The DCG factory + * @param EventDispatcherInterface $dispatcher The event dispatcher. + * @param TranslatorInterface $translator The translator. + * @param ContaoFramework $framework The Contao framework + * + * @return Response + */ + public function __invoke( + Request $request, + TwigEnvironment $twig, + DcGeneralFactoryService $factoryFactory, + EventDispatcherInterface $dispatcher, + TranslatorInterface $translator, + ContaoFramework $framework, + ViewCombination $viewCombination, + ): Response { + $containerName = (string) $request->query->get('table', ''); + if ('' === $containerName) { + $containerName = (string) ($request->attributes->get('_route_params', [])['tableName'] ?? ''); + } + $combination = $viewCombination->getCombination($containerName); + if (null === $combination) { + throw new AccessDeniedException('Permission denied to access back end module "' . $containerName . '".'); + } + $inputScreenId = $combination['dca_id'] ?? ''; + $controllerResult = $this->bootDcGeneralAndProcess( + $request, + $containerName, + $factoryFactory, + $dispatcher, + $translator, + $framework + ); + $headline = $this->determineHeadline($containerName, $inputScreenId, $translator); + + return new Response( + $twig->render( + '@MetaModelsCore/Backend/be_config.html.twig', + [ + 'headline' => $headline, + 'body' => $controllerResult, + 'stylesheets' => ['bundles/metamodelscore/css/style.css'] + ] + ) + ); + } + + /** + * Generate headline. + * + * @param string $containerName The container. + * @param string $inputScreenId The input screen id. + * @param TranslatorInterface $translator The translator. + * + * @return string + */ + private function determineHeadline( + string $containerName, + string $inputScreenId, + TranslatorInterface $translator + ): string { + return $translator->translate('backend-module.' . $inputScreenId . '.headline', $containerName); + } +} diff --git a/src/CoreBundle/Controller/Backend/RenderSettingAddAllController.php b/src/CoreBundle/Controller/Backend/RenderSettingAddAllController.php index 83431be7f..effff2e9f 100644 --- a/src/CoreBundle/Controller/Backend/RenderSettingAddAllController.php +++ b/src/CoreBundle/Controller/Backend/RenderSettingAddAllController.php @@ -49,7 +49,7 @@ public function __invoke($metaModel, $renderSetting, Request $request) * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - protected function createEmptyDataFor(IAttribute $attribute, $parentId, $activate, $sort) + protected function createEmptyDataFor(IAttribute $attribute, $parentId, $activate, $sort, $tlclass = '') { $result = [ 'attr_id' => $attribute->get('id'), diff --git a/src/CoreBundle/Controller/Backend/SupportMetaModelsController.php b/src/CoreBundle/Controller/Backend/SupportMetaModelsController.php index 68c004008..7eef890e2 100644 --- a/src/CoreBundle/Controller/Backend/SupportMetaModelsController.php +++ b/src/CoreBundle/Controller/Backend/SupportMetaModelsController.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2022 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2022 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,7 @@ namespace MetaModels\CoreBundle\Controller\Backend; use Symfony\Component\HttpFoundation\Response; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; use Twig\Environment as TwigEnvironment; /** @@ -88,20 +89,20 @@ public function __invoke() 'stylesheets' => [ 'bundles/metamodelscore/css/supportscreen.css' ], - 'headline' => $this->translator->trans('MOD.support_metamodels.0', [], 'contao_modules'), + 'headline' => $this->translator->trans('menu.label', [], 'metamodels_support'), 'sub_headline' => - $this->translator->trans('MSC.metamodels_support.main_headline', [], 'contao_default'), + $this->translator->trans('main_headline', [], 'metamodels_support'), 'head_contributor' => - $this->translator->trans('MSC.metamodels_support.contributor_headline', [], 'contao_default'), - 'purpose' => $this->translator->trans('MSC.metamodels_support.purpose', [], 'contao_default'), + $this->translator->trans('contributor_headline', [], 'metamodels_support'), + 'purpose' => $this->translator->trans('purpose', [], 'metamodels_support'), 'other_donations' => - $this->translator->trans('MSC.metamodels_support.other_donations', [], 'contao_default'), + $this->translator->trans('other_donations', [], 'metamodels_support'), 'main_text' => - $this->translator->trans('MSC.metamodels_support.main_text', [], 'contao_default'), + $this->translator->trans('main_text', [], 'metamodels_support'), 'help_headline' => - $this->translator->trans('MSC.metamodels_support.help_headline', [], 'contao_default'), + $this->translator->trans('help_headline', [], 'metamodels_support'), 'help_text' => - $this->translator->trans('MSC.metamodels_support.help_text', [], 'contao_default'), + $this->translator->trans('help_text', [], 'metamodels_support'), 'github_contributors' => $this->getJsonFile($this->github), 'transifex_contributors' => $this->getJsonFile($this->transifex) ] diff --git a/src/CoreBundle/Controller/ContentElement/ItemListController.php b/src/CoreBundle/Controller/ContentElement/ItemListController.php index 0180b7258..f73870b28 100644 --- a/src/CoreBundle/Controller/ContentElement/ItemListController.php +++ b/src/CoreBundle/Controller/ContentElement/ItemListController.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Ingolf Steinhardt * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +26,7 @@ use Contao\CoreBundle\Controller\ContentElement\AbstractContentElementController; use Contao\CoreBundle\ServiceAnnotation\ContentElement; use Contao\PageModel; +use Contao\System; use Contao\Template; use MetaModels\CoreBundle\Controller\ListControllerTrait; use Symfony\Component\HttpFoundation\Request; @@ -35,6 +36,8 @@ * The item list content element. * * @ContentElement("metamodel_content", category="metamodels", template="ce_metamodel_content") + * + * @psalm-suppress PropertyNotSetInConstructor */ final class ItemListController extends AbstractContentElementController { @@ -58,11 +61,12 @@ public function __invoke( array $classes = null, PageModel $pageModel = null ): Response { - if ($this->get('contao.routing.scope_matcher')->isBackendRequest($request)) { + if ($this->scopeMatcher->isBackendRequest($request)) { return $this->getBackendWildcard($model); } - if (!empty($model->metamodel_layout)) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + if (null !== $model->metamodel_layout) { $model->customTpl = $model->metamodel_layout; } @@ -78,7 +82,7 @@ public function __invoke( * * @return Response The response. */ - protected function getResponse(Template $template, ContentModel $model, Request $request): ?Response + protected function getResponse(Template $template, ContentModel $model, Request $request): Response { $response = $this->getResponseInternal($template, $model, $request); $this->addSharedMaxAgeToResponse($response, $model); @@ -95,8 +99,8 @@ protected function getResponse(Template $template, ContentModel $model, Request */ private function getBackendWildcard(ContentModel $model): Response { - $name = $this->get('translator')->trans('CTE.' . $this->getType() . '.0', [], 'contao_modules'); - $href = $this->get('router')->generate( + $name = $this->translator->trans($this->getType(), [], 'metamodels_wildcard'); + $href = $this->router->generate( 'contao_backend', ['do' => 'article', 'table' => 'tl_content', 'act' => 'edit', 'id' => $model->id] ); diff --git a/src/CoreBundle/Controller/FrontendModule/ItemListController.php b/src/CoreBundle/Controller/FrontendModule/ItemListController.php index 35fff15e7..c5f4f0839 100644 --- a/src/CoreBundle/Controller/FrontendModule/ItemListController.php +++ b/src/CoreBundle/Controller/FrontendModule/ItemListController.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Richard Henkenjohann * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2020 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -34,6 +35,8 @@ * The item list front end module. * * @FrontendModule("metamodel_list", category="metamodels") + * + * @psalm-suppress PropertyNotSetInConstructor */ final class ItemListController extends AbstractFrontendModuleController { @@ -57,7 +60,12 @@ public function __invoke( array $classes = null, PageModel $pageModel = null ): Response { - if (!empty($model->metamodel_layout)) { + if ($this->scopeMatcher->isBackendRequest($request)) { + return $this->getBackendWildcard($model); + } + + /** @psalm-suppress UndefinedMagicPropertyFetch */ + if (null !== $model->metamodel_layout) { $model->customTpl = $model->metamodel_layout; } @@ -73,8 +81,8 @@ public function __invoke( */ protected function getBackendWildcard(ModuleModel $module): Response { - $name = $this->get('translator')->trans('FMD.'.$this->getType().'.0', [], 'contao_modules'); - $href = $this->get('router')->generate( + $name = $this->translator->trans($this->getType(), [], 'metamodels_wildcard'); + $href = $this->router->generate( 'contao_backend', ['do' => 'themes', 'table' => 'tl_module', 'act' => 'edit', 'id' => $module->id] ); @@ -91,7 +99,7 @@ protected function getBackendWildcard(ModuleModel $module): Response * * @return Response The response. */ - protected function getResponse(Template $template, ModuleModel $model, Request $request): ?Response + protected function getResponse(Template $template, ModuleModel $model, Request $request): Response { return $this->getResponseInternal($template, $model, $request); } diff --git a/src/CoreBundle/Controller/ListControllerTrait.php b/src/CoreBundle/Controller/ListControllerTrait.php index 0b6c546bd..6c0359785 100644 --- a/src/CoreBundle/Controller/ListControllerTrait.php +++ b/src/CoreBundle/Controller/ListControllerTrait.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Christian Schiffler * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,20 +23,26 @@ namespace MetaModels\CoreBundle\Controller; use Contao\BackendTemplate; +use Contao\CoreBundle\Routing\ScopeMatcher; use Contao\Input; use Contao\Model; use Contao\StringUtil; +use Contao\System; use Contao\Template; use MetaModels\Filter\FilterUrl; use MetaModels\Filter\FilterUrlBuilder; use MetaModels\Filter\Setting\IFilterSettingFactory; +use MetaModels\Helper\SortingLinkGenerator; use MetaModels\IFactory; use MetaModels\IItem; +use MetaModels\IMetaModel; use MetaModels\ItemList; use MetaModels\Render\Setting\IRenderSettingFactory; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\Routing\RouterInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * Helper trait for lists (CE and MOD). @@ -78,6 +84,27 @@ trait ListControllerTrait */ private FilterUrlBuilder $filterUrlBuilder; + /** + * The translator. + * + * @var TranslatorInterface + */ + private TranslatorInterface $translator; + + /** + * The router. + * + * @var RouterInterface + */ + private RouterInterface $router; + + /** + * The scope matcher. + * + * @var ScopeMatcher + */ + private ScopeMatcher $scopeMatcher; + /** * ItemListController constructor. * @@ -86,19 +113,64 @@ trait ListControllerTrait * @param IRenderSettingFactory $renderSettingFactory The render setting factory (required in MetaModels 3.0). * @param EventDispatcherInterface $eventDispatcher The event dispatcher (required in MetaModels 3.0). * @param FilterUrlBuilder $filterUrlBuilder The filter url builder. + * @param TranslatorInterface|null $translator The translator. + * @param RouterInterface|null $router The router. + * @param ScopeMatcher|null $scopeMatcher The scope matcher. */ public function __construct( IFactory $factory, IFilterSettingFactory $filterFactory, IRenderSettingFactory $renderSettingFactory, EventDispatcherInterface $eventDispatcher, - FilterUrlBuilder $filterUrlBuilder + FilterUrlBuilder $filterUrlBuilder, + TranslatorInterface $translator = null, + RouterInterface $router = null, + ScopeMatcher $scopeMatcher = null ) { $this->factory = $factory; $this->filterFactory = $filterFactory; $this->renderSettingFactory = $renderSettingFactory; $this->eventDispatcher = $eventDispatcher; $this->filterUrlBuilder = $filterUrlBuilder; + + if (null === $translator) { + // @codingStandardsIgnoreStart + @trigger_error( + 'Translator is missing. It has to be passed in the constructor. Fallback will be dropped.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + $translator = System::getContainer()->get('translator'); + assert($translator instanceof TranslatorInterface); + } + $this->translator = $translator; + + if (null === $router) { + // @codingStandardsIgnoreStart + @trigger_error( + 'Router is missing. It has to be passed in the constructor. Fallback will be dropped.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + $router = System::getContainer()->get('router'); + assert($router instanceof RouterInterface); + } + $this->router = $router; + + if (null === $scopeMatcher) { + // @codingStandardsIgnoreStart + @trigger_error( + 'ScopeMatcher is missing. It has to be passed in the constructor. Fallback will be dropped.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + $scopeMatcher = System::getContainer()->get('contao.routing.scope_matcher'); + assert($scopeMatcher instanceof ScopeMatcher); + } + $this->scopeMatcher = $scopeMatcher; } /** @@ -112,10 +184,14 @@ public function __construct( * * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ - private function getResponseInternal(Template $template, Model $model, Request $request): ?Response + private function getResponseInternal(Template $template, Model $model, Request $request): Response { - if (empty($pageParam = $model->metamodel_page_param)) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + if ('' === ($pageParam = $model->metamodel_page_param)) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ switch ($model->type) { case 'metamodel_content': $pageParam = 'page_mmce' . $model->id; @@ -128,6 +204,7 @@ private function getResponseInternal(Template $template, Model $model, Request $ } } + /** @psalm-suppress UndefinedMagicPropertyFetch */ $itemRenderer = new ItemList( $this->factory, $this->filterFactory, @@ -141,35 +218,59 @@ private function getResponseInternal(Template $template, Model $model, Request $ $model->metamodel_pagination_urlfragment ); + /** + * @psalm-suppress UndefinedMagicPropertyAssignment + * @psalm-suppress UndefinedMagicPropertyFetch + */ $template->searchable = !$model->metamodel_donotindex; - $sorting = $model->metamodel_sortby; + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $sorting = $model->metamodel_sortby; + /** @psalm-suppress UndefinedMagicPropertyFetch */ $direction = $model->metamodel_sortby_direction; + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $sortParamType = $model->metamodel_sort_param_type; + $sortOrderByParam = 'orderBy'; + $sortOrderDirParam = 'orderDir'; + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $sortOverride = $model->metamodel_sort_override; + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $sortFragment = $model->metamodel_sort_urlfragment; // @codingStandardsIgnoreStart // FIXME: filter URL should be created from local request and not from master request. // @codingStandardsIgnoreEnd $filterUrl = $this->filterUrlBuilder->getCurrentFilterUrl(); - if ($model->metamodel_sort_override) { - if (null !== + if ($sortOverride) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $sortOrderByParam = $model->metamodel_order_by_param ?: $sortOrderByParam; + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $sortOrderDirParam = $model->metamodel_order_dir_param ?: $sortOrderDirParam; + if ( + null !== $value = $this->tryReadFromSlugOrGet( $filterUrl, - ($model->metamodel_order_by_param ?: 'orderBy'), - $model->metamodel_sort_param_type - )) { + $sortOrderByParam, + $sortParamType + ) + ) { $sorting = $value; } - if (null !== + if ( + null !== $value = $this->tryReadFromSlugOrGet( $filterUrl, - ($model->metamodel_order_dir_param ?: 'orderDir'), - $model->metamodel_sort_param_type - )) { + $sortOrderDirParam, + $sortParamType + ) + ) { $direction = $value; } } + /** @psalm-suppress UndefinedMagicPropertyFetch */ $filterParams = StringUtil::deserialize($model->metamodel_filterparams, true); + /** @psalm-suppress UndefinedMagicPropertyFetch */ $itemRenderer ->setMetaModel($model->metamodel, $model->metamodel_rendersettings) ->setListTemplate($template) @@ -179,20 +280,45 @@ private function getResponseInternal(Template $template, Model $model, Request $ ->setFilterSettings($model->metamodel_filtering) ->setFilterParameters($filterParams, $this->getFilterParameters($filterUrl, $itemRenderer)) ->setMetaTags($model->metamodel_meta_title, $model->metamodel_meta_description); + if ($sortOverride) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $itemRenderer->setSortingLinkGenerator( + new SortingLinkGenerator( + $this->filterUrlBuilder, + $this->translator, + $sortParamType, + $sortOrderByParam, + $sortOrderDirParam, + $sortFragment, + $model->metamodel_sortby, + $model->metamodel_sortby_direction + ) + ); + } - foreach (StringUtil::deserialize(($model->metamodel_parameters ?? null), true) as $key => $value) { - $itemRenderer->setTemplateParameter($key, $value); + /** @psalm-suppress UndefinedMagicPropertyFetch */ + if ($model->metamodel_use_parameters) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + foreach (StringUtil::deserialize(($model->metamodel_parameters ?? null), true) as $key => $value) { + $itemRenderer->setTemplateParameter($key, $value); + } } + /** + * @psalm-suppress UndefinedMagicPropertyAssignment + * @psalm-suppress UndefinedMagicPropertyFetch + */ $template->items = StringUtil::encodeEmail($itemRenderer->render($model->metamodel_noparsing, $model)); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->numberOfItems = $itemRenderer->getItems()->getCount(); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->pagination = $itemRenderer->getPagination(); - $responseTags = array_map( + $responseTags = \array_map( static function (IItem $item) { - return sprintf('contao.db.%s.%d', $item->getMetaModel()->getTableName(), $item->get('id')); + return \sprintf('contao.db.%s.%d', $item->getMetaModel()->getTableName(), $item->get('id')); }, - iterator_to_array($itemRenderer->getItems(), false) + \iterator_to_array($itemRenderer->getItems(), false) ); $response = $template->getResponse(); @@ -267,11 +393,14 @@ private function renderBackendWildcard(string $href, string $name, Model $model) { $template = new BackendTemplate('be_wildcard'); + /** @psalm-suppress UndefinedMagicPropertyFetch */ $headline = StringUtil::deserialize($model->headline); - + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->wildcard = $this->getWildcardInfoText($model, $href, $name); - $template->title = (\is_array($headline) ? $headline['value'] : $headline); - $template->id = $model->id; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->title = (\is_array($headline) ? $headline['value'] : $headline); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->id = $model->id; return new Response($template->parse()); } @@ -292,67 +421,73 @@ private function renderBackendWildcard(string $href, string $name, Model $model) */ private function getWildcardInfoText(Model $model, string $href, string $name): string { - if (empty($model->metamodel)) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + if (null === $model->metamodel) { return 'MetaModel not configured.'; } - if (null === $metaModelName = $this->factory->translateIdToMetaModelName($model->metamodel)) { + if ('' === ($metaModelName = $this->factory->translateIdToMetaModelName($model->metamodel))) { return 'Unknown MetaModel: ' . $model->metamodel; } // Add CSS file. $GLOBALS['TL_CSS'][] = 'bundles/metamodelscore/css/style.css'; - // Retrieve name of MetaModels. + // Retrieve name of MetaModel. $infoTemplate = '
%s: %s
'; $metaModel = $this->factory->getMetaModel($metaModelName); - $header = $name . ': ' . $metaModel->getName(); + assert($metaModel instanceof IMetaModel); + + $header = $metaModel->getName(); if ($href) { - $header .= sprintf( + $header .= \sprintf( ' (ID: %3$s)', $href, - REQUEST_TOKEN, - $model->id + System::getContainer()->get('contao.csrf.token_manager')?->getDefaultTokenValue() ?? '', + (string) $model->id ); } - $infoText = sprintf( + $infoText = '### ' . $name . ' ###' . \sprintf( $infoTemplate, - $this->get('translator')->trans('MSC.mm_be_info_name.1', [], 'contao_default'), - $this->get('translator')->trans('MSC.mm_be_info_name.0', [], 'contao_default'), - $header + $this->translator->trans('mm_be_info_name.description', [], 'metamodels_wildcard'), + $this->translator->trans('mm_be_info_name.label', [], 'metamodels_wildcard'), + $header, ); // Retrieve name of filter. + /** @psalm-suppress UndefinedMagicPropertyFetch */ if ($model->metamodel_filtering) { $filterparams = []; + /** @psalm-suppress UndefinedMagicPropertyFetch */ foreach (StringUtil::deserialize($model->metamodel_filterparams, true) as $filterparam) { if ($filterparam['value']) { $filterparams[] = $filterparam['value']; } } - $infoFiPa = count($filterparams) ? ': ' . implode(', ', $filterparams) : ''; + $infoFiPa = \count($filterparams) ? ': ' . \implode(', ', $filterparams) : ''; $infoFi = $this->filterFactory->createCollection($model->metamodel_filtering)->get('name'); - if ($infoFi) { - $infoText .= sprintf( + if (null !== $infoFi) { + $infoText .= \sprintf( $infoTemplate, - $this->get('translator')->trans('MSC.mm_be_info_filter.1', [], 'contao_default'), - $this->get('translator')->trans('MSC.mm_be_info_filter.0', [], 'contao_default'), + $this->translator->trans('mm_be_info_filter.description', [], 'metamodels_wildcard'), + $this->translator->trans('mm_be_info_filter.label', [], 'metamodels_wildcard'), $infoFi . $infoFiPa ); } } - // Retrieve name of rendersetting. + // Retrieve name of render setting. + /** @psalm-suppress UndefinedMagicPropertyFetch */ if ($model->metamodel_rendersettings) { $infoRs = $this->renderSettingFactory ->createCollection($metaModel, $model->metamodel_rendersettings) ->get('name'); - if ($infoRs) { - $infoText .= sprintf( + if (null !== $infoRs) { + $infoText .= \sprintf( $infoTemplate, - $this->get('translator')->trans('MSC.mm_be_info_render_setting.1', [], 'contao_default'), - $this->get('translator')->trans('MSC.mm_be_info_render_setting.0', [], 'contao_default'), + $this->translator->trans('mm_be_info_render_setting.description', [], 'metamodels_wildcard'), + $this->translator->trans('mm_be_info_render_setting.label', [], 'metamodels_wildcard'), $infoRs ); } diff --git a/src/CoreBundle/DcGeneral/AbstractAttributeConditionFactory.php b/src/CoreBundle/DcGeneral/AbstractAttributeConditionFactory.php index 82ec437b5..efb20328a 100644 --- a/src/CoreBundle/DcGeneral/AbstractAttributeConditionFactory.php +++ b/src/CoreBundle/DcGeneral/AbstractAttributeConditionFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/core * @author Christian Schiffler - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,8 +31,7 @@ abstract class AbstractAttributeConditionFactory implements AttributeAwareProper * Extract the attribute instance from the MetaModel. * * @param IMetaModel $metaModel The MetaModel instance. - * - * @param string $attributeId The attribute id. + * @param int $attributeId The attribute id. * * @return string * diff --git a/src/CoreBundle/DcGeneral/AbstractRestrictedAttributeConditionFactory.php b/src/CoreBundle/DcGeneral/AbstractRestrictedAttributeConditionFactory.php index 4f472a68a..79641839a 100644 --- a/src/CoreBundle/DcGeneral/AbstractRestrictedAttributeConditionFactory.php +++ b/src/CoreBundle/DcGeneral/AbstractRestrictedAttributeConditionFactory.php @@ -23,6 +23,7 @@ * This is the abstract base for attribute aware condition factories. * * @SuppressWarnings(PHPMD.LongClassName) + * @SuppressWarnings(PHPMD.LongVariable) */ abstract class AbstractRestrictedAttributeConditionFactory extends AbstractAttributeConditionFactory { diff --git a/src/CoreBundle/DcGeneral/FallbackPropertyConditionFactory.php b/src/CoreBundle/DcGeneral/FallbackPropertyConditionFactory.php index d368959d8..c497c98a9 100644 --- a/src/CoreBundle/DcGeneral/FallbackPropertyConditionFactory.php +++ b/src/CoreBundle/DcGeneral/FallbackPropertyConditionFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2021 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 */ @@ -37,7 +38,7 @@ class FallbackPropertyConditionFactory * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * Create a new instance. @@ -158,7 +159,9 @@ public function supportsAttribute($conditionType, $attribute) */ public function createCondition(array $configuration, IMetaModel $metaModel) { + /** @psalm-suppress DeprecatedClass */ $event = new CreatePropertyConditionEvent($configuration, $metaModel); + /** @psalm-suppress DeprecatedClass */ $this->dispatcher->dispatch($event, CreatePropertyConditionEvent::NAME); if (null === $instance = $event->getInstance()) { diff --git a/src/CoreBundle/DcGeneral/PropertyConditionFactory.php b/src/CoreBundle/DcGeneral/PropertyConditionFactory.php index fd37f0181..77b4ade10 100644 --- a/src/CoreBundle/DcGeneral/PropertyConditionFactory.php +++ b/src/CoreBundle/DcGeneral/PropertyConditionFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/core * @author Christian Schiffler - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,8 @@ /** * This factory takes care of building property conditions. + * + * @psalm-suppress DeprecatedClass */ class PropertyConditionFactory { @@ -33,20 +36,22 @@ class PropertyConditionFactory * * @var IdProvidingServiceLocator */ - private $factories; + private IdProvidingServiceLocator $factories; /** * The fallback factory. * * @var FallbackPropertyConditionFactory */ - private $fallbackFactory; + private FallbackPropertyConditionFactory $fallbackFactory; /** * Create a new instance. * * @param IdProvidingServiceLocator $factories The factories. * @param FallbackPropertyConditionFactory $fallbackFactory The fallback factory. + * + * @psalm-suppress DeprecatedClass */ public function __construct(IdProvidingServiceLocator $factories, FallbackPropertyConditionFactory $fallbackFactory) { @@ -138,11 +143,7 @@ public function createCondition(array $configuration, IMetaModel $metaModel) } if (!$this->factories->has($typeName = $configuration['type'])) { - if ($result = $this->fallbackFactory->createCondition($configuration, $metaModel)) { - return $result; - } - - throw new \InvalidArgumentException('Unknown type: ' . $typeName); + return $this->fallbackFactory->createCondition($configuration, $metaModel); } return $this->getFactory($typeName)->buildCondition($configuration, $metaModel); diff --git a/src/CoreBundle/DependencyInjection/CompilerPass/CollectDoctrineSchemaGeneratorsPass.php b/src/CoreBundle/DependencyInjection/CompilerPass/CollectDoctrineSchemaGeneratorsPass.php new file mode 100644 index 000000000..d4493a479 --- /dev/null +++ b/src/CoreBundle/DependencyInjection/CompilerPass/CollectDoctrineSchemaGeneratorsPass.php @@ -0,0 +1,49 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\CoreBundle\DependencyInjection\CompilerPass; + +use MetaModels\Schema\Doctrine\DoctrineSchemaGenerator; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This pass adds the tagged schema generators to the doctrine engine. + */ +class CollectDoctrineSchemaGeneratorsPass implements CompilerPassInterface +{ + use PriorityTaggedServiceTrait; + + public const TAG_NAME = 'metamodels.schema-generator.doctrine'; + + /** + * {@inheritDoc} + */ + public function process(ContainerBuilder $container): void + { + $generator = $container->getDefinition(DoctrineSchemaGenerator::class); + $generator->setArgument( + 0, + array_merge($generator->getArgument(0), $this->findAndSortTaggedServices(self::TAG_NAME, $container)) + ); + } +} diff --git a/src/CoreBundle/DependencyInjection/CompilerPass/CollectFactoriesPass.php b/src/CoreBundle/DependencyInjection/CompilerPass/CollectFactoriesPass.php index 7996dd543..a43d7453a 100644 --- a/src/CoreBundle/DependencyInjection/CompilerPass/CollectFactoriesPass.php +++ b/src/CoreBundle/DependencyInjection/CompilerPass/CollectFactoriesPass.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,22 +33,22 @@ class CollectFactoriesPass implements CompilerPassInterface /** * The tag name to use for attribute factories. */ - const TAG_ATTRIBUTE_FACTORY = 'metamodels.attribute_factory'; + public const TAG_ATTRIBUTE_FACTORY = 'metamodels.attribute_factory'; /** * The tag name to use for filter factories. */ - const TAG_FILTER_FACTORY = 'metamodels.filter_factory'; + public const TAG_FILTER_FACTORY = 'metamodels.filter_factory'; /** * The tag name to use for property condition factories. */ - const TAG_PROPERTY_CONDITION_FACTORY = 'metamodels.condition_factory'; + public const TAG_PROPERTY_CONDITION_FACTORY = 'metamodels.condition_factory'; /** * {@inheritDoc} */ - public function process(ContainerBuilder $container) + public function process(ContainerBuilder $container): void { $this->collectAttributeFactories($container); $this->collectFilterFactories($container); @@ -61,10 +62,10 @@ public function process(ContainerBuilder $container) * * @return void */ - private function collectAttributeFactories($container) + private function collectAttributeFactories(ContainerBuilder $container): void { $attributeFactory = $container->getDefinition('metamodels.attribute_factory'); - foreach (array_keys($container->findTaggedServiceIds(self::TAG_ATTRIBUTE_FACTORY)) as $factory) { + foreach (\array_keys($container->findTaggedServiceIds(self::TAG_ATTRIBUTE_FACTORY)) as $factory) { $attributeFactory->addMethodCall('addTypeFactory', [new Reference($factory)]); } } @@ -76,10 +77,10 @@ private function collectAttributeFactories($container) * * @return void */ - private function collectFilterFactories($container) + private function collectFilterFactories(ContainerBuilder $container): void { $attributeFactory = $container->getDefinition('metamodels.filter_setting_factory'); - foreach (array_keys($container->findTaggedServiceIds(self::TAG_FILTER_FACTORY)) as $factory) { + foreach (\array_keys($container->findTaggedServiceIds(self::TAG_FILTER_FACTORY)) as $factory) { $attributeFactory->addMethodCall('addTypeFactory', [new Reference($factory)]); } } @@ -91,7 +92,7 @@ private function collectFilterFactories($container) * * @return void */ - private function collectPropertyConditionFactories($container) + private function collectPropertyConditionFactories(ContainerBuilder $container): void { $factories = $container->getDefinition('metamodels.core_bundle.dc_general.property_condition_factories'); $args = $factories->getArgument(0); diff --git a/src/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaGeneratorsPass.php b/src/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaGeneratorsPass.php new file mode 100644 index 000000000..6bf79dfa5 --- /dev/null +++ b/src/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaGeneratorsPass.php @@ -0,0 +1,49 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\CoreBundle\DependencyInjection\CompilerPass; + +use MetaModels\Schema\SchemaGenerator; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This pass adds the tagged schema generators. + */ +class CollectSchemaGeneratorsPass implements CompilerPassInterface +{ + use PriorityTaggedServiceTrait; + + public const TAG_NAME = 'metamodels.schema-generator'; + + /** + * {@inheritDoc} + */ + public function process(ContainerBuilder $container): void + { + $generator = $container->getDefinition(SchemaGenerator::class); + $generator->setArgument( + 0, + array_merge($generator->getArgument(0), $this->findAndSortTaggedServices(self::TAG_NAME, $container)) + ); + } +} diff --git a/src/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaManagersPass.php b/src/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaManagersPass.php new file mode 100644 index 000000000..e76815c52 --- /dev/null +++ b/src/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaManagersPass.php @@ -0,0 +1,49 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\CoreBundle\DependencyInjection\CompilerPass; + +use MetaModels\Schema\SchemaManager; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\Compiler\PriorityTaggedServiceTrait; +use Symfony\Component\DependencyInjection\ContainerBuilder; + +/** + * This pass adds the tagged schema managers. + */ +class CollectSchemaManagersPass implements CompilerPassInterface +{ + use PriorityTaggedServiceTrait; + + public const TAG_NAME = 'metamodels.schema-manager'; + + /** + * {@inheritDoc} + */ + public function process(ContainerBuilder $container): void + { + $generator = $container->getDefinition(SchemaManager::class); + $generator->setArgument( + 0, + array_merge($generator->getArgument(0), $this->findAndSortTaggedServices(self::TAG_NAME, $container)) + ); + } +} diff --git a/src/CoreBundle/DependencyInjection/CompilerPass/PrepareTranslatorPass.php b/src/CoreBundle/DependencyInjection/CompilerPass/PrepareTranslatorPass.php new file mode 100644 index 000000000..be83bc0ba --- /dev/null +++ b/src/CoreBundle/DependencyInjection/CompilerPass/PrepareTranslatorPass.php @@ -0,0 +1,47 @@ + + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\CoreBundle\DependencyInjection\CompilerPass; + +use MetaModels\CoreBundle\Translator\MetaModelTranslatorConfigurator; +use Symfony\Bundle\FrameworkBundle\Translation\Translator; +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; + +final class PrepareTranslatorPass implements CompilerPassInterface +{ + public function process(ContainerBuilder $container): void + { + $definition = $container->getDefinition('translator.default'); + if (Translator::class !== $definition->getClass()) { + return; + } + $previousConfigurator = $definition->getConfigurator(); + $definition->setConfigurator(new Reference(MetaModelTranslatorConfigurator::class)); + if (null !== $previousConfigurator) { + $container->getDefinition(MetaModelTranslatorConfigurator::class)->setArgument( + '$previous', + $previousConfigurator + ); + } + } +} diff --git a/src/CoreBundle/DependencyInjection/Configuration.php b/src/CoreBundle/DependencyInjection/Configuration.php index a72d9b4a4..e040e8daa 100644 --- a/src/CoreBundle/DependencyInjection/Configuration.php +++ b/src/CoreBundle/DependencyInjection/Configuration.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -22,7 +23,7 @@ use Symfony\Component\Config\Definition\Builder\TreeBuilder; use Symfony\Component\Config\Definition\ConfigurationInterface; -use Webmozart\PathUtil\Path; +use Symfony\Component\Filesystem\Path; /** * Adds the Contao configuration structure. @@ -34,14 +35,14 @@ class Configuration implements ConfigurationInterface * * @var bool */ - private $debug; + private bool $debug; /** * The root directory. * * @var string */ - private $rootDir; + private string $rootDir; /** * Constructor. @@ -51,7 +52,7 @@ class Configuration implements ConfigurationInterface */ public function __construct($debug, $rootDir) { - $this->debug = (bool) $debug; + $this->debug = $debug; $this->rootDir = $rootDir; } @@ -59,13 +60,15 @@ public function __construct($debug, $rootDir) * Generates the configuration tree builder. * * @return TreeBuilder + * + * @psalm-suppress UndefinedMethod */ public function getConfigTreeBuilder() { - $treeBuilder = new TreeBuilder(); - $rootNode = $treeBuilder->root('metamodels'); + $treeBuilder = new TreeBuilder('metamodels'); - $rootNode + $treeBuilder + ->getRootNode() ->children() ->booleanNode('enable_cache') ->defaultValue(!$this->debug) @@ -77,7 +80,7 @@ public function getConfigTreeBuilder() ->cannotBeEmpty() ->defaultValue($this->resolvePath($this->rootDir . '/assets/metamodels')) ->validate() - ->always(function ($value) { + ->always(function (string $value): string { return $this->resolvePath($value); }) ->end() @@ -98,12 +101,12 @@ public function getConfigTreeBuilder() * * @return string */ - private function resolvePath($value) + private function resolvePath(string $value): string { $path = Path::canonicalize($value); if ('\\' === DIRECTORY_SEPARATOR) { - $path = str_replace('/', '\\', $path); + $path = \str_replace('/', '\\', $path); } return $path; diff --git a/src/CoreBundle/DependencyInjection/IdProvidingServiceLocator.php b/src/CoreBundle/DependencyInjection/IdProvidingServiceLocator.php index 767041299..c1dc2d39c 100644 --- a/src/CoreBundle/DependencyInjection/IdProvidingServiceLocator.php +++ b/src/CoreBundle/DependencyInjection/IdProvidingServiceLocator.php @@ -17,7 +17,7 @@ * @filesource */ -declare(strict_types = 1); +declare(strict_types=1); namespace MetaModels\CoreBundle\DependencyInjection; diff --git a/src/CoreBundle/DependencyInjection/MetaModelsCoreExtension.php b/src/CoreBundle/DependencyInjection/MetaModelsCoreExtension.php index cddb6d7ee..1aa37c4f7 100644 --- a/src/CoreBundle/DependencyInjection/MetaModelsCoreExtension.php +++ b/src/CoreBundle/DependencyInjection/MetaModelsCoreExtension.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,20 +16,22 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author Richard Henkenjohann - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\DependencyInjection; -use Doctrine\Common\Cache\ArrayCache; use MetaModels\CoreBundle\Migration\TableCollationMigration; +use MetaModels\Filter\FilterUrlBuilder; +use Symfony\Component\Cache\Adapter\ArrayAdapter; use Symfony\Component\Config\FileLocator; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\DependencyInjection\Extension\Extension; use Symfony\Component\DependencyInjection\Extension\PrependExtensionInterface; use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; +use Symfony\Component\DependencyInjection\Reference; /** * This is the class that loads and manages the bundle configuration @@ -41,7 +43,7 @@ class MetaModelsCoreExtension extends Extension implements PrependExtensionInter * * @var array */ - private $defaultTableOptions; + private array $defaultTableOptions = []; /** * The configuration files. @@ -86,14 +88,16 @@ public function prepend(ContainerBuilder $container): void /** * {@inheritDoc} */ - public function load(array $configs, ContainerBuilder $container) + public function load(array $configs, ContainerBuilder $container): void { $loader = new YamlFileLoader($container, new FileLocator(__DIR__ . '/../Resources/config')); foreach (self::$files as $file) { $loader->load($file); } - $config = $this->processConfiguration($this->getConfiguration($configs, $container), $configs); + $configuration = $this->getConfiguration($configs, $container); + assert($configuration instanceof Configuration); + $config = $this->processConfiguration($configuration, $configs); $this->buildCacheService($container, $config); $container->setParameter('metamodels.resource_dir', __DIR__ . '/../Resources'); @@ -102,6 +106,11 @@ public function load(array $configs, ContainerBuilder $container) $container->getDefinition(TableCollationMigration::class) ->setArgument('$defaultTableOptions', $this->defaultTableOptions); + + if ((bool) $container->getParameter('contao.legacy_routing')) { + $container->getDefinition(FilterUrlBuilder::class) + ->setArgument(0, new Reference('contao.routing.url_generator')); + } } /** @@ -109,10 +118,9 @@ public function load(array $configs, ContainerBuilder $container) */ public function getConfiguration(array $config, ContainerBuilder $container) { - return new Configuration( - $container->getParameter('kernel.debug'), - $container->getParameter('kernel.project_dir') - ); + $projectDir = $container->getParameter('kernel.project_dir'); + assert(\is_string($projectDir)); + return new Configuration((bool) $container->getParameter('kernel.debug'), $projectDir); } /** @@ -123,12 +131,12 @@ public function getConfiguration(array $config, ContainerBuilder $container) * * @return void */ - private function buildCacheService(ContainerBuilder $container, array $config) + private function buildCacheService(ContainerBuilder $container, array $config): void { - // if cache disabled, swap it out with the dummy cache. + // If cache disabled, swap it out with the dummy cache. if (!$config['enable_cache']) { - $cache = $container->getDefinition('metamodels.cache'); - $cache->setClass(ArrayCache::class); + $cache = $container->getDefinition('metamodels.cache_internal'); + $cache->setClass(ArrayAdapter::class); $cache->setArguments([]); $container->setParameter('metamodels.cache_dir', null); return; @@ -138,7 +146,7 @@ private function buildCacheService(ContainerBuilder $container, array $config) } /** - * Collect the default table options from the dcotrine extension. + * Collect the default table options from the doctrine extension. * * @param ContainerBuilder $container The container builder. * diff --git a/src/CoreBundle/EventListener/AttributeAddingListener.php b/src/CoreBundle/EventListener/AttributeAddingListener.php index 1fc3ff400..9cd90fd34 100644 --- a/src/CoreBundle/EventListener/AttributeAddingListener.php +++ b/src/CoreBundle/EventListener/AttributeAddingListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class AttributeAddingListener * * @var IAttributeFactory */ - private $attributeFactory; + private IAttributeFactory $attributeFactory; /** * Create a new instance. diff --git a/src/CoreBundle/EventListener/BackendNavigationListener.php b/src/CoreBundle/EventListener/BackendNavigationListener.php index 200b150a9..93c240aeb 100644 --- a/src/CoreBundle/EventListener/BackendNavigationListener.php +++ b/src/CoreBundle/EventListener/BackendNavigationListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,25 +14,34 @@ * @author Christian Schiffler * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener; -use Contao\BackendUser; use Contao\CoreBundle\Event\MenuEvent; -use Contao\StringUtil; +use Knp\Menu\FactoryInterface; +use Knp\Menu\ItemInterface; use MetaModels\ViewCombination\ViewCombination; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\Session; use Symfony\Component\Routing\RouterInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; -use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function array_key_exists; +use function array_map; +use function in_array; +use function is_array; /** * This registers the backend navigation of MetaModels. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class BackendNavigationListener { @@ -48,28 +57,35 @@ class BackendNavigationListener * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * The translator in use. * * @var ViewCombination */ - private $viewCombination; + private ViewCombination $viewCombination; /** * The token storage. * * @var TokenStorageInterface */ - private $tokenStorage; + private TokenStorageInterface $tokenStorage; /** * The router. * * @var RouterInterface */ - private $router; + private RouterInterface $router; + + /** + * The session. + * + * @var Session + */ + private Session $session; /** * Create a new instance. @@ -79,19 +95,22 @@ class BackendNavigationListener * @param ViewCombination $viewCombination The view combination. * @param TokenStorageInterface $tokenStorage The token storage. * @param RouterInterface $router The router. + * @param Session $session The session. */ public function __construct( TranslatorInterface $translator, RequestStack $requestStack, ViewCombination $viewCombination, TokenStorageInterface $tokenStorage, - RouterInterface $router + RouterInterface $router, + Session $session, ) { $this->requestStack = $requestStack; $this->translator = $translator; $this->viewCombination = $viewCombination; $this->tokenStorage = $tokenStorage; $this->router = $router; + $this->session = $session; } /** @@ -113,40 +132,38 @@ public function __invoke(MenuEvent $event): void return; } - if (null === $request = $this->requestStack->getCurrentRequest()) { + if (null === ($request = $this->requestStack->getCurrentRequest())) { + return; + } + + if (null === ($user = $this->tokenStorage->getToken())) { return; } $this->addBackendCss(); - if (null !== ($user = $this->tokenStorage->getToken())) { - $userRights = $this->extractUserRights($user); - } + $isAdmin = in_array('ROLE_ADMIN', $user->getRoleNames(), true); - $isAdmin = \in_array('ROLE_ADMIN', $user->getRoleNames(), true); + $metaModelsNode = $this->getRootNode($tree, $factory); - $metaModelsNode = $tree->getChild('metamodels'); - if (null !== $metaModelsNode && ($isAdmin || isset($userRights['support_metamodels']))) { - $node = $factory - ->createItem('support_screen') - ->setUri($this->router->generate('metamodels.support_screen')) - ->setLabel($this->translator->trans('MOD.support_metamodels.0', [], 'contao_modules')) - ->setLinkAttribute('title', $this->translator->trans('MOD.support_metamodels.1', [], 'contao_modules')) - ->setLinkAttribute('class', 'support_screen') - ->setCurrent('metamodels.support_screen' === $request->get('_route')); + $names = array_map(static fn(ItemInterface $item): string => $item->getName(), $metaModelsNode->getChildren()); - $metaModelsNode->addChild($node); + // Show MetaModels config only for Admins. + if ($isAdmin) { + $metaModelsNode->addChild($configNode = $this->buildConfigNode($factory, $request)); + array_unshift($names, $configNode->getName()); } - $locale = $request->getLocale(); - foreach ($this->viewCombination->getStandalone() as $metaModelName => $screen) { - $moduleName = 'metamodel_' . $metaModelName; - if (!$isAdmin && !isset($userRights[$moduleName])) { - continue; - } + $metaModelsNode->reorderChildren($names); + + $currentMetaModel = ''; + if ($request->attributes->get('_route') === 'metamodels.metamodel') { + $currentMetaModel = (string) (($request->attributes->get('_route_params', []))['tableName'] ?? ''); + } - $sectionNode = $tree->getChild($screen['meta']['backendsection']); - if (null === $sectionNode) { + foreach ($this->viewCombination->getStandalone() as $metaModelName => $screen) { + if (null === $sectionNode = $this->getSectionNode($factory, $tree, $screen['meta']['backendsection'])) { + // Rien ne vas plus. continue; } @@ -154,69 +171,232 @@ public function __invoke(MenuEvent $event): void $node = $factory ->createItem($item) - ->setUri($this->router->generate('contao_backend', ['do' => $item])) - ->setLabel($this->extractLanguageValue($screen['label'], $locale)) - ->setLinkAttribute('title', $this->extractLanguageValue($screen['description'], $locale)) + ->setUri($this->router->generate('metamodels.metamodel', ['tableName' => $metaModelName])) + ->setLabel('inputscreen.' . $screen['meta']['id'] . '.menu.label') + ->setExtra('translation_domain', $metaModelName) + ->setLinkAttribute( + 'title', + $this->translator->trans( + 'inputscreen.' . $screen['meta']['id'] . '.menu.description', + [], + $metaModelName + ) + ) ->setLinkAttribute('class', $item) - ->setCurrent($request->get('_route') === 'contao_backend' && $request->query->get('do') === $item); + ->setCurrent($currentMetaModel === $metaModelName); $sectionNode->addChild($node); } + + if ($isAdmin) { + $node = $factory + ->createItem('support_screen') + ->setUri($this->router->generate('metamodels.support_screen')) + ->setLabel('menu.label') + ->setExtra('translation_domain', 'metamodels_support') + ->setLinkAttribute('title', $this->translator->trans('menu.description', [], 'metamodels_support')) + ->setLinkAttribute('class', 'support_screen') + ->setCurrent('metamodels.support_screen' === $request->attributes->get('_route')); + + $metaModelsNode->addChild($node); + } } /** - * Extract the permissions from the Contao backend user. + * Add the CSS files for the backend. * - * @param TokenInterface $token The token. + * @return void * - * @return array + * @SuppressWarnings(PHPMD.Superglobals) + * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - private function extractUserRights(TokenInterface $token): array + private function addBackendCss(): void { - $beUser = $token->getUser(); - if (!($beUser instanceof BackendUser)) { - return []; - } + // BE group icon. + $GLOBALS['TL_CSS']['metamodels'] = 'bundles/metamodelscore/css/be_logo_svg.css'; + } - $allowedModules = $beUser->modules; - switch (true) { - case \is_string($allowedModules): - $allowedModules = StringUtil::deserialize($allowedModules, true); - break; - case null === $allowedModules: - $allowedModules = []; - break; - default: + /** + * Get root node. + * + * @param ItemInterface $tree The tree. + * @param FactoryInterface $factory The factory. + * + * @return ItemInterface + */ + private function getRootNode(ItemInterface $tree, FactoryInterface $factory): ItemInterface + { + $names = + $this->getChildNamesFromTree($tree); + $insertPos = (int) array_search('accounts', $names, true) + 1; + $metaModelsNode = $tree->getChild('metamodels'); + if (null === $metaModelsNode) { + $metaModelsNode = $factory->createItem('metamodels'); + $tree->addChild($metaModelsNode); } - return array_flip($allowedModules); + $metaModelsNode + ->setLabel('menuGroup.metamodels.label') + ->setExtra('translation_domain', 'metamodels_navigation'); + + $this->updateCollapsedState($metaModelsNode); + + // Resort if already existing. + $names = array_values(array_filter($names, static fn(string $name): bool => 'metamodels' !== $name)); + + array_splice($names, $insertPos, 0, 'metamodels'); + $tree->reorderChildren($names); + + return $metaModelsNode; } /** - * Extract the language value. + * Generate build config. * - * @param string[] $values The values. + * @param FactoryInterface $factory The factory. + * @param Request $request The request. * - * @param string $locale The current locale. - * - * @return string + * @return ItemInterface */ - private function extractLanguageValue($values, $locale): string + private function buildConfigNode(FactoryInterface $factory, Request $request): ItemInterface { - return html_entity_decode(($values[$locale] ?? $values[''])); + $configNode = $factory->createItem('metamodels'); + + $configNode + ->setUri($this->router->generate('metamodels.configuration')) + ->setLabel('menu.metamodels.label') + ->setExtra('translation_domain', 'metamodels_navigation') + ->setLinkAttribute('title', $this->translator->trans('menu.metamodels.title', [], 'metamodels_navigation')) + ->setLinkAttribute('class', 'metamodel_config') + ->setCurrent('metamodels.configuration' === $request->attributes->get('_route')); + + return $configNode; } /** - * Add the CSS files for the backend. + * Update collapsed state of navigation groups. + * + * @param ItemInterface $metaModelsNode * * @return void + */ + private function updateCollapsedState(ItemInterface $metaModelsNode): void + { + $nodeName = $metaModelsNode->getName(); + $sessionBag = $this->session->getBag('contao_backend'); + $status = ($sessionBag instanceof AttributeBagInterface) ? $sessionBag->get('backend_modules') : []; + $isCollapsed = ($status[$nodeName] ?? 1) < 1; + $path = $this->router->generate('contao_backend'); + + $metaModelsNode + ->setLinkAttribute('class', 'group-' . $nodeName) + ->setLinkAttribute( + 'onclick', + "return AjaxRequest.toggleNavigation(this, '" . $nodeName . "', '" . $path . "')" + ) + ->setLinkAttribute('aria-controls', $nodeName) + ->setChildrenAttribute('id', $nodeName) + ->setLinkAttribute( + 'title', + $this->translator->trans('MSC.' . ($isCollapsed ? 'expand' : 'collapse') . 'Node', [], 'contao_default') + ) + ->setLinkAttribute('aria-expanded', $isCollapsed ? 'false' : 'true'); + + if ($isCollapsed) { + $metaModelsNode->setAttribute('class', 'collapsed'); + } + + $request = $this->requestStack->getCurrentRequest(); + if (null === $request) { + return; + } + + $uri = $this->router->generate( + 'contao_backend', + [ + 'do' => $request->query->get('do'), + 'mtg' => $nodeName, + 'ref' => $request->attributes->get('_contao_referer_id') + ] + ); + + $metaModelsNode->setUri($uri); + } + + /** + * Get section node. + * + * @param FactoryInterface $factory The factory. + * @param ItemInterface $tree The item interface. + * @param string $backendSection The backend section. + * + * @return ItemInterface|null * * @SuppressWarnings(PHPMD.Superglobals) - * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - private function addBackendCss(): void + private function getSectionNode( + FactoryInterface $factory, + ItemInterface $tree, + string $backendSection + ): ?ItemInterface { + if (null !== $sectionNode = $tree->getChild($backendSection)) { + return $sectionNode; + } + + // Somehow it disappeared - try to generate it via BE_MOD. + if (!array_key_exists($backendSection, $GLOBALS['BE_MOD'])) { + return null; + } + $navigation = array_keys($GLOBALS['BE_MOD']); + // Keep child names before adding the new child. + $namesInMenu = $this->getChildNamesFromTree($tree); + $sectionNode = $factory->createItem($backendSection); + $tree->addChild($sectionNode); + $sectionNode + ->setLabel($this->getLabelForSection($backendSection)) + ->setExtra('translation_domain', false); + + $this->updateCollapsedState($sectionNode); + + // Search the position in the already existing menu by starting at offset in BE_MOD and walking up to the start. + $start = (int) array_search($backendSection, $navigation, true); + while (0 <= --$start) { + /** @psalm-suppress InvalidArrayOffset */ + if (in_array($navigation[$start], $namesInMenu, true)) { + array_splice($namesInMenu, $start + 1, 0, [$backendSection]); + break; + } + } + // If we did not find a position in existing menu, append at the end. + if (!in_array($backendSection, $namesInMenu)) { + $namesInMenu[] = $backendSection; + } + $tree->reorderChildren($namesInMenu); + + return $sectionNode; + } + + /** + * @param ItemInterface $tree + * + * @return list + */ + private function getChildNamesFromTree(ItemInterface $tree): array { - // BE group icon. - $GLOBALS['TL_CSS']['metamodels'] = 'bundles/metamodelscore/css/be_logo_svg.css'; + return array_values( + array_map(static fn(ItemInterface $item): string => $item->getName(), $tree->getChildren()) + ); + } + + /** @SuppressWarnings(PHPMD.Superglobals) */ + public function getLabelForSection(string $backendSection): string + { + /** @var null|list|string $langValue */ + $langValue = $GLOBALS['TL_LANG']['MOD'][$backendSection] ?? null; + if (is_array($langValue)) { + return $langValue[0] ?? $backendSection; + } + + return $langValue ?? $backendSection; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/AbstractBreadcrumbListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/AbstractBreadcrumbListener.php index 1d18c6015..2dcc30a56 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/AbstractBreadcrumbListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/AbstractBreadcrumbListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -23,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * This class renders various breadcrumbs. @@ -34,14 +36,14 @@ abstract class AbstractBreadcrumbListener * * @var BreadcrumbStoreFactory */ - private $storeFactory; + private BreadcrumbStoreFactory $storeFactory; /** * The parent element renderer. * - * @var AbstractBreadcrumbListener + * @var AbstractBreadcrumbListener|null */ - private $parent; + private ?AbstractBreadcrumbListener $parent; /** * Create a new instance. @@ -90,7 +92,6 @@ abstract protected function wantToHandle(GetBreadcrumbEvent $event); * Perform the bread crumb generating. * * @param EnvironmentInterface $environment The environment in use. - * * @param BreadcrumbStore $elements The elements generated so far. * * @return void @@ -106,15 +107,17 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea * Extract the id value from the serialized parameter with the given name. * * @param EnvironmentInterface $environment The environment. - * * @param string $parameterName The parameter name containing the id. * - * @return int + * @return string */ protected function extractIdFrom(EnvironmentInterface $environment, $parameterName = 'pid') { - $parameter = $environment->getInputProvider()->getParameter($parameterName); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $parameter = $inputProvider->getParameter($parameterName); - return ModelId::fromSerialized($parameter)->getId(); + return (string) ModelId::fromSerialized($parameter)->getId(); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbAttributeListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbAttributeListener.php index 13e789233..33a12e5c3 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbAttributeListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbAttributeListener.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. @@ -13,20 +13,25 @@ * @package MetaModels/core * @author Christian Schiffler * @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\CoreBundle\EventListener\DcGeneral\Breadcrumb; +use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** * Generate a breadcrumb for table tl_metamodel_attribute. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbAttributeListener extends AbstractBreadcrumbListener { @@ -37,7 +42,10 @@ class BreadcrumbAttributeListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_attribute' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_attribute' === $dataDefinition->getName(); } /** @@ -61,11 +69,12 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $modelId = $elements->getId('tl_metamodel'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_attribute'), - $this->getMetaModel($elements->getId('tl_metamodel'))->getName() + (null !== $modelId) ? $this->getMetaModel($modelId)->getName() : '' ), 'bundles/metamodelscore/images/icons/fields.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaCombineListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaCombineListener.php index 8d7cc11fe..6038beb91 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaCombineListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaCombineListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,20 +13,25 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb; +use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** * Generate a breadcrumb for table tl_metamodel_dcacombine. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbDcaCombineListener extends AbstractBreadcrumbListener { @@ -37,7 +42,10 @@ class BreadcrumbDcaCombineListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_dca_combine' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_dca_combine' === $dataDefinition->getName(); } /** @@ -61,11 +69,12 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $modelId = $elements->getId('tl_metamodel'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_dca_combine'), - $this->getMetaModel($elements->getId('tl_metamodel'))->getName() + (null !== $modelId) ? $this->getMetaModel($modelId)->getName() : '' ), 'bundles/metamodelscore/images/icons/dca_combine.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaListener.php index e87abbba3..9b6946407 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,20 +13,25 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb; +use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** * Generate a breadcrumb for table tl_metamodel_dca. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbDcaListener extends AbstractBreadcrumbListener { @@ -38,7 +43,10 @@ class BreadcrumbDcaListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_dca' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_dca' === $dataDefinition->getName(); } /** @@ -52,7 +60,7 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea } else { $elements->setId( 'tl_metamodel', - $this->getRow($elements->getId('tl_metamodel_dca'), 'tl_metamodel_dca')->pid + $this->getRow($elements->getId('tl_metamodel_dca') ?? '', 'tl_metamodel_dca')->pid ); } } @@ -69,11 +77,12 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $modelId = $elements->getId('tl_metamodel'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_dca'), - $this->getMetaModel($elements->getId('tl_metamodel'))->getName() + (null !== $modelId) ? $this->getMetaModel($modelId)->getName() : '' ), 'bundles/metamodelscore/images/icons/dca.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSettingConditionListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSettingConditionListener.php index e5d05c2f2..bd38c2c46 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSettingConditionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSettingConditionListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,11 +24,16 @@ use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; +use MetaModels\Helper\LocaleUtil; +use MetaModels\IMetaModel; /** * Generate a breadcrumb for table tl_metamodel_dcasetting_condition. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbDcaSettingConditionListener extends AbstractBreadcrumbListener { @@ -40,7 +45,10 @@ class BreadcrumbDcaSettingConditionListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_dcasetting_condition' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_dcasetting_condition' === $dataDefinition->getName(); } /** @@ -67,11 +75,12 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $dcaSettingId = $elements->getId('tl_metamodel_dcasetting'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_dcasetting_condition'), - $this->getConditionAttribute($elements->getId('tl_metamodel_dcasetting')) + (null !== $dcaSettingId) ? $this->getConditionAttribute($dcaSettingId) : '' ), 'bundles/metamodelscore/images/icons/dca_condition.png' ); @@ -91,16 +100,19 @@ private function getConditionAttribute($settingId) { $setting = $this->getRow($settingId, 'tl_metamodel_dcasetting'); - if ($setting->dcatype == 'attribute') { - $attribute = (object) $this->getRow($setting->attr_id, 'tl_metamodel_attribute'); + if ($setting->dcatype === 'attribute') { + $attribute = $this->getRow($setting->attr_id, 'tl_metamodel_attribute'); $metaModelName = $this->factory->translateIdToMetaModelName($attribute->pid); - $attribute = $this->factory->getMetaModel($metaModelName)->getAttributeById($attribute->id); + $metaModel = $this->factory->getMetaModel($metaModelName); + assert($metaModel instanceof IMetaModel); + $attribute = $metaModel->getAttributeById((int) $attribute->id); if ($attribute) { return $attribute->getName(); } } else { $title = StringUtil::deserialize($setting->legendtitle, true); - return ($title[\str_replace('-', '_', $GLOBALS['TL_LANGUAGE'])] ?? current($title)); + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + return ($title[LocaleUtil::formatAsLocale($GLOBALS['TL_LANGUAGE'])] ?? current($title)); } return 'unknown ' . $setting->dcatype; diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSettingListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSettingListener.php index 7e88c2e57..b6ae9e430 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSettingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSettingListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,20 +13,25 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb; +use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** * Generate a breadcrumb for table tl_metamodel_dcasetting. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbDcaSettingListener extends AbstractBreadcrumbListener { @@ -37,7 +42,10 @@ class BreadcrumbDcaSettingListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_dcasetting' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_dcasetting' === $dataDefinition->getName(); } /** @@ -51,7 +59,7 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea } else { $elements->setId( 'tl_metamodel_dca', - $this->getRow($elements->getId('tl_metamodel_dcasetting'), 'tl_metamodel_dcasetting')->pid + $this->getRow($elements->getId('tl_metamodel_dcasetting') ?? '', 'tl_metamodel_dcasetting')->pid ); } } @@ -69,11 +77,12 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $dcaId = $elements->getId('tl_metamodel_dca'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_dcasetting'), - $this->getRow($elements->getId('tl_metamodel_dca'), 'tl_metamodel_dca')->name + (null !== $dcaId) ? $this->getRow($dcaId, 'tl_metamodel_dca')->name : '' ), 'bundles/metamodelscore/images/icons/dca_setting.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSortGroupListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSortGroupListener.php index 6dd33b7df..90b2896c5 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSortGroupListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbDcaSortGroupListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,20 +13,25 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb; +use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** * Generate a breadcrumb for table tl_metamodel_dca_sortgroup. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbDcaSortGroupListener extends AbstractBreadcrumbListener { @@ -37,7 +42,10 @@ class BreadcrumbDcaSortGroupListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_dca_sortgroup' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_dca_sortgroup' === $dataDefinition->getName(); } /** @@ -62,11 +70,12 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $dcaId = $elements->getId('tl_metamodel_dca'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_dca_sortgroup'), - $this->getRow($elements->getId('tl_metamodel_dca'), 'tl_metamodel_dca')->name + (null !== $dcaId) ? $this->getRow($dcaId, 'tl_metamodel_dca')->name : '' ), 'bundles/metamodelscore/images/icons/dca_groupsortsettings.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbFilterListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbFilterListener.php index 2f5a8c96c..b52a93c5e 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbFilterListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbFilterListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,20 +13,25 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb; +use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** * Generate a breadcrumb for table tl_metamodel_filter. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbFilterListener extends AbstractBreadcrumbListener { @@ -38,7 +43,10 @@ class BreadcrumbFilterListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_filter' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_filter' === $dataDefinition->getName(); } /** @@ -52,7 +60,7 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea } else { $elements->setId( 'tl_metamodel', - $this->getRow($elements->getId('tl_metamodel_filter'), 'tl_metamodel_filter')->pid + $this->getRow($elements->getId('tl_metamodel_filter') ?? '', 'tl_metamodel_filter')->pid ); } } @@ -69,11 +77,12 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $modelId = $elements->getId('tl_metamodel'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_filter'), - $this->getMetaModel($elements->getId('tl_metamodel'))->getName() + (null !== $modelId) ? $this->getMetaModel($modelId)->getName() : '' ), 'bundles/metamodelscore/images/icons/filter.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbFilterSettingListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbFilterSettingListener.php index 4ae117bcf..35cfcb1bb 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbFilterSettingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbFilterSettingListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,20 +13,25 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb; +use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** * Generate a breadcrumb for table tl_metamodel_dcasetting. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbFilterSettingListener extends AbstractBreadcrumbListener { @@ -37,7 +42,10 @@ class BreadcrumbFilterSettingListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_filtersetting' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_filtersetting' === $dataDefinition->getName(); } /** @@ -51,7 +59,10 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea } else { $elements->setId( 'tl_metamodel_filter', - $this->getRow($elements->getId('tl_metamodel_filtersetting'), 'tl_metamodel_filtersetting')->pid + $this->getRow( + $elements->getId('tl_metamodel_filtersetting') ?? '', + 'tl_metamodel_filtersetting' + )->pid ); } } @@ -69,11 +80,12 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $filterId = $elements->getId('tl_metamodel_filter'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_filtersetting'), - $this->getRow($elements->getId('tl_metamodel_filter'), 'tl_metamodel_filter')->name + (null !== $filterId) ? $this->getRow($filterId, 'tl_metamodel_filter')->name : '' ), 'bundles/metamodelscore/images/icons/filter_setting.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbMetaModelListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbMetaModelListener.php index 75c356ec3..a996ab0d7 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbMetaModelListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbMetaModelListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,10 +22,13 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; /** * Generate a breadcrumb for table tl_metamodel. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbMetaModelListener extends AbstractBreadcrumbListener { @@ -35,7 +39,10 @@ class BreadcrumbMetaModelListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel' === $dataDefinition->getName(); } /** @@ -44,7 +51,7 @@ protected function wantToHandle(GetBreadcrumbEvent $event) protected function getBreadcrumbElements(EnvironmentInterface $environment, BreadcrumbStore $elements) { $elements->push( - 'contao/main.php?do=metamodels', + 'contao/metamodels', 'tl_metamodel', 'bundles/metamodelscore/images/backend/logo.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbRenderSettingListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbRenderSettingListener.php index 3d2a6a105..27afffca9 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbRenderSettingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbRenderSettingListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,20 +13,25 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb; +use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** * Generate a breadcrumb for table tl_metamodel_rendersetting. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbRenderSettingListener extends AbstractBreadcrumbListener { @@ -37,7 +42,10 @@ class BreadcrumbRenderSettingListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_rendersetting' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_rendersetting' === $dataDefinition->getName(); } /** @@ -64,11 +72,15 @@ public function getBreadcrumbElements(EnvironmentInterface $environment, Breadcr ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $renderSettingsId = $elements->getId('tl_metamodel_rendersettings'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_rendersetting'), - $this->getRow($elements->getId('tl_metamodel_rendersettings'), 'tl_metamodel_rendersettings')->name + (null !== $renderSettingsId) ? $this->getRow( + $renderSettingsId, + 'tl_metamodel_rendersettings' + )->name : '' ), 'bundles/metamodelscore/images/icons/rendersetting.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbRenderSettingsListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbRenderSettingsListener.php index 91d70b43c..923f88508 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbRenderSettingsListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbRenderSettingsListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,20 +13,25 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb; +use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** * Generate a breadcrumb for table tl_metamodel_rendersettings. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbRenderSettingsListener extends AbstractBreadcrumbListener { @@ -38,7 +43,10 @@ class BreadcrumbRenderSettingsListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_rendersettings' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_rendersettings' === $dataDefinition->getName(); } /** @@ -52,7 +60,10 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea } else { $elements->setId( 'tl_metamodel', - $this->getRow($elements->getId('tl_metamodel_rendersettings'), 'tl_metamodel_rendersettings')->pid + $this->getRow( + $elements->getId('tl_metamodel_rendersettings') ?? '', + 'tl_metamodel_rendersettings' + )->pid ); } } @@ -69,11 +80,12 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $modelId = $elements->getId('tl_metamodel'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_rendersettings'), - $this->getMetaModel($elements->getId('tl_metamodel'))->getName() + (null !== $modelId) ? $this->getMetaModel($modelId)->getName() : '' ), 'bundles/metamodelscore/images/icons/rendersettings.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbSearchablePagesListener.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbSearchablePagesListener.php index 0b83e1d9c..4d5dd3784 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbSearchablePagesListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbSearchablePagesListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,20 +13,25 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Breadcrumb; +use Contao\StringUtil; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** * Generate a breadcrumb for table tl_metamodel_searchable_pages. + * + * @psalm-suppress PropertyNotSetInConstructor */ class BreadcrumbSearchablePagesListener extends AbstractBreadcrumbListener { @@ -38,7 +43,10 @@ class BreadcrumbSearchablePagesListener extends AbstractBreadcrumbListener */ protected function wantToHandle(GetBreadcrumbEvent $event) { - return 'tl_metamodel_searchable_pages' === $event->getEnvironment()->getDataDefinition()->getName(); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return 'tl_metamodel_searchable_pages' === $dataDefinition->getName(); } /** @@ -53,7 +61,7 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea $elements->setId( 'tl_metamodel', $this->getRow( - $elements->getId('tl_metamodel_searchable_pages'), + $elements->getId('tl_metamodel_searchable_pages') ?? '', 'tl_metamodel_searchable_pages' )->pid ); @@ -75,11 +83,12 @@ protected function getBreadcrumbElements(EnvironmentInterface $environment, Brea ->unsetQueryParameter('act') ->unsetQueryParameter('id'); + $modelId = $elements->getId('tl_metamodel'); $elements->push( - ampersand($builder->getUrl()), - sprintf( + StringUtil::ampersand($builder->getUrl()), + \sprintf( $elements->getLabel('tl_metamodel_searchable_pages'), - $this->getMetaModel($elements->getId('tl_metamodel'))->getName() + (null !== $modelId) ? $this->getMetaModel($modelId)->getName() : '' ), 'bundles/metamodelscore/images/icons/searchable_pages.png' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbStore.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbStore.php index 3040ff26a..2e73cab3e 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbStore.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbStore.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,12 @@ use Contao\StringUtil; use MetaModels\CoreBundle\Assets\IconBuilder; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function array_key_exists; +use function str_replace; +use function str_starts_with; +use function ucfirst; /** * The breadcrumb store. @@ -34,35 +40,35 @@ class BreadcrumbStore * * @var array */ - private $elements = []; + private array $elements = []; /** * The icon builder. * * @var IconBuilder */ - private $iconBuilder; + private IconBuilder $iconBuilder; /** * The translator. * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * List of "current" ids. * * @var string[] */ - private $idList = []; + private array $idList = []; /** * The current URI. * * @var string */ - private $uri; + private string $uri; /** * Create a new instance. @@ -144,19 +150,19 @@ public function getUri(): string } /** - * Get for a table the human readable name or a fallback. + * Get for a table the human-readable name or a fallback. * * @param string $table Name of table. * - * @return string The human readable name. + * @return string The human-readable name. */ public function getLabel($table): string { - if (strpos($table, 'tl_') !== 0) { + if (!str_starts_with($table, 'tl_')) { return $table; } $shortTable = str_replace('tl_', '', $table); - $label = $this->translator->trans('BRD.' . $shortTable, [], 'contao_default'); + $label = $this->translator->trans($shortTable, [], 'metamodels_navigation'); if ($label === $shortTable) { $shortTable = str_replace('tl_metamodel_', '', $table); return ucfirst($shortTable) . ' %s'; diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbStoreFactory.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbStoreFactory.php index d82f43e66..86cc1de55 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbStoreFactory.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/BreadcrumbStoreFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2022 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2022 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,7 @@ use MetaModels\CoreBundle\Assets\IconBuilder; use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This class creates an instance of a breadcrumb store. diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/ConnectionTrait.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/ConnectionTrait.php index 9ad8b6788..88db17130 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/ConnectionTrait.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/ConnectionTrait.php @@ -65,7 +65,7 @@ private function getRow($rowId, $table) ->where('t.id=:id') ->setParameter('id', $rowId) ->setMaxResults(1) - ->execute() - ->fetch(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAssociative(); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/GetMetaModelTrait.php b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/GetMetaModelTrait.php index 4d7e52c5e..4ea5f68aa 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/GetMetaModelTrait.php +++ b/src/CoreBundle/EventListener/DcGeneral/Breadcrumb/GetMetaModelTrait.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ trait GetMetaModelTrait * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Retrieve the MetaModel instance. @@ -46,12 +47,13 @@ trait GetMetaModelTrait */ protected function getMetaModel($metaModelId) { - if (null === $this->factory) { - throw new \RuntimeException('No factory set.'); - } - $metaModelName = $this->factory->translateIdToMetaModelName($metaModelId); - $metaModel = $this->factory->getMetaModel($metaModelName); + + $metaModel = $this->factory->getMetaModel($metaModelName); + + if (null === $metaModel) { + throw new \RuntimeException('MetaModel not found'); + } return $metaModel; } @@ -59,7 +61,7 @@ protected function getMetaModel($metaModelId) /** * {@inheritDoc} */ - public function setMetaModelFactory(IFactory $factory) + public function setMetaModelFactory(IFactory $factory): void { $this->factory = $factory; } diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/AbstractConditionBuilder.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/AbstractConditionBuilder.php index 89050be84..6026d0fe1 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/AbstractConditionBuilder.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/AbstractConditionBuilder.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. @@ -13,16 +13,14 @@ * @package MetaModels/core * @author Christian Schiffler * @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\CoreBundle\EventListener\DcGeneral\DefinitionBuilder; -use Contao\Input; -use ContaoCommunityAlliance\DcGeneral\Data\ModelId; -use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefaultModelRelationshipDefinition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\ModelRelationshipDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; @@ -33,7 +31,9 @@ use MetaModels\DcGeneral\DataDefinition\IMetaModelDataDefinition; /** - * This class is the abstract base for the condition builders. + * This class is the abstract base for the hierarchical/variant model condition builders. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractConditionBuilder { @@ -42,21 +42,21 @@ abstract class AbstractConditionBuilder * * @var IMetaModelDataDefinition */ - protected $container; + protected IMetaModelDataDefinition $container; /** * The input screen. * * @var array */ - protected $inputScreen; + protected array $inputScreen; /** * The model relationship interface. * * @var ModelRelationshipDefinitionInterface */ - protected $definition; + protected ModelRelationshipDefinitionInterface $definition; /** * Parse the correct conditions. @@ -82,14 +82,25 @@ public static function calculateConditions(IMetaModelDataDefinition $container, throw new \InvalidArgumentException('Search element does not implement the correct interface.'); } - $instance = new static(); - - $instance->container = $container; - $instance->inputScreen = $inputScreen; - $instance->definition = $definition; + $instance = new static($container, $inputScreen, $definition); $instance->calculate(); } + /** + * @param IMetaModelDataDefinition $container + * @param array $inputScreen + * @param ModelRelationshipDefinitionInterface $definition + */ + final public function __construct( + IMetaModelDataDefinition $container, + array $inputScreen, + ModelRelationshipDefinitionInterface $definition + ) { + $this->container = $container; + $this->inputScreen = $inputScreen; + $this->definition = $definition; + } + /** * The real calculating function. * @@ -97,14 +108,14 @@ public static function calculateConditions(IMetaModelDataDefinition $container, * * @throws \RuntimeException When the conditions can not be determined. */ - abstract protected function calculate(); + abstract protected function calculate(): void; /** * Parse the correct conditions for a MetaModel with variant support. * * @return void */ - protected function addParentCondition() + protected function addParentCondition(): void { if ($this->inputScreen['meta']['rendertype'] === 'standalone') { return; @@ -125,8 +136,8 @@ protected function addParentCondition() ->setDestinationName($this->container->getName()); $this->definition->addChildCondition($relationship); } else { - $setter = array_merge_recursive($setter, $relationship->getSetters()); - $inverse = array_merge_recursive($inverse, $relationship->getInverseFilterArray()); + $setter = \array_merge_recursive($setter, $relationship->getSetters()); + $inverse = \array_merge_recursive($inverse, $relationship->getInverseFilterArray()); } // For tl_ prefix, the only unique target can be the id? @@ -143,73 +154,11 @@ protected function addParentCondition() } /** - * Parse the correct conditions for a MetaModel with variant support. - * - * @return void - */ - protected function addHierarchicalConditions() - { - // Not hierarchical? Get out. - if ($this->container->getBasicDefinition()->getMode() !== BasicDefinitionInterface::MODE_HIERARCHICAL) { - return; - } - - $relationship = $this->getRootCondition(); - - // NOTE: this might bear problems when the definition will get serialized as the input value will not change. - if (Input::get('pid')) { - $parentValue = ModelId::fromSerialized(Input::get('pid'))->getId(); - } else { - $parentValue = '0'; - } - - if (!$relationship->getSetters()) { - $relationship - ->setSetters([['property' => 'pid', 'value' => $parentValue]]); - } - - $builder = FilterBuilder::fromArrayForRoot((array) $relationship->getFilterArray())->getFilter(); - - $builder->andPropertyEquals('pid', $parentValue); - - $relationship - ->setFilterArray($builder->getAllAsArray()); - - $setter = [['to_field' => 'pid', 'from_field' => 'id']]; - $inverse = []; - - /** @var ParentChildConditionInterface $relationship */ - $relationship = $this->definition->getChildCondition($this->container->getName(), $this->container->getName()); - if ($relationship === null) { - $relationship = new ParentChildCondition(); - $relationship - ->setSourceName($this->container->getName()) - ->setDestinationName($this->container->getName()); - $this->definition->addChildCondition($relationship); - } else { - $setter = array_merge_recursive($setter, $relationship->getSetters()); - $inverse = array_merge_recursive($inverse, $relationship->getInverseFilterArray()); - } - - // For tl_ prefix, the only unique target can be the id? - // maybe load parent dc and scan for unique in config then. - $relationship - ->setFilterArray( - FilterBuilder::fromArray($relationship->getFilterArray()) - ->getFilter() - ->andRemotePropertyEquals('pid', 'id') - ->getAllAsArray() - ) - ->setSetters($setter) - ->setInverseFilterArray($inverse); - } - - /** - * Parse the correct conditions for a MetaModel with variant support. + * Parse the root conditions for a MetaModel with hierarchical/variant support. * * @return RootConditionInterface */ - protected function getRootCondition() + protected function getRootCondition(): RootConditionInterface { $rootProvider = $this->container->getName(); diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/BasicDefinitionBuilder.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/BasicDefinitionBuilder.php index f210800e9..5390dcbfc 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/BasicDefinitionBuilder.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/BasicDefinitionBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +25,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefaultBasicDefinition; use MetaModels\DcGeneral\DataDefinition\IMetaModelDataDefinition; use MetaModels\IFactory; +use MetaModels\IMetaModel; use MetaModels\ViewCombination\ViewCombination; /** @@ -39,14 +40,14 @@ class BasicDefinitionBuilder * * @var ViewCombination */ - private $viewCombination; + private ViewCombination $viewCombination; /** * The factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Create a new instance. @@ -70,7 +71,7 @@ public function __construct(ViewCombination $viewCombination, IFactory $factory) protected function build(IMetaModelDataDefinition $container) { $inputScreen = $this->viewCombination->getScreen($container->getName()); - if (!$inputScreen) { + if (null === $inputScreen) { return; } $meta = $inputScreen['meta']; @@ -80,6 +81,7 @@ protected function build(IMetaModelDataDefinition $container) $config->setDataProvider($container->getName()); $metaModel = $this->factory->getMetaModel($container->getName()); + assert($metaModel instanceof IMetaModel); // If we have variants, override all modes to tree mode. if ($metaModel->hasVariants()) { $config->setMode(BasicDefinitionInterface::MODE_HIERARCHICAL); @@ -113,7 +115,7 @@ protected function build(IMetaModelDataDefinition $container) * * @return BasicDefinitionInterface */ - private function getOrCreateBasicDefinition(IMetaModelDataDefinition $container) + private function getOrCreateBasicDefinition(IMetaModelDataDefinition $container): BasicDefinitionInterface { if ($container->hasBasicDefinition()) { return $container->getBasicDefinition(); diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/CommandBuilder.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/CommandBuilder.php index 4e70036b2..8dbf8db1d 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/CommandBuilder.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/CommandBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,10 +37,12 @@ use MetaModels\IMetaModel; use MetaModels\ViewCombination\ViewCombination; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This class builds the commands. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CommandBuilder { @@ -51,49 +53,42 @@ class CommandBuilder * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * The view combinations. * * @var ViewCombination */ - private $viewCombination; + private ViewCombination $viewCombination; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The container (only set during build phase). * - * @var IMetaModelDataDefinition - */ - private $container; - - /** - * The input screen (only set during build phase). - * - * @var array + * @var IMetaModelDataDefinition|null */ - private $inputScreen; + private IMetaModelDataDefinition|null $container = null; /** * The icon builder. * * @var IconBuilder */ - private $iconBuilder; + private IconBuilder $iconBuilder; /** * The translator. * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * Create a new instance. @@ -142,15 +137,14 @@ protected function build(IMetaModelDataDefinition $container) ); } - $this->container = $container; - $this->inputScreen = $inputScreen = $this->viewCombination->getScreen($container->getName()); + $this->container = $container; + $inputScreen = $this->viewCombination->getScreen($container->getName()); if (null === $inputScreen) { return; } $this->addEditMultipleCommand($view); $this->parseModelOperations($view); - $this->container = null; - $this->inputScreen = null; + $this->container = null; if ($this->dispatcher->hasListeners(BuildMetaModelOperationsEvent::NAME)) { // @codingStandardsIgnoreStart @@ -159,11 +153,9 @@ protected function build(IMetaModelDataDefinition $container) E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $event = new BuildMetaModelOperationsEvent( - $this->factory->getMetaModel($container->getName()), - $container, - $inputScreen - ); + $metaModel = $this->factory->getMetaModel($container->getName()); + assert($metaModel instanceof IMetaModel); + $event = new BuildMetaModelOperationsEvent($metaModel, $container, $inputScreen); $this->dispatcher->dispatch($event, $event::NAME); } } @@ -175,8 +167,9 @@ protected function build(IMetaModelDataDefinition $container) * * @return void */ - private function addEditMultipleCommand(Contao2BackendViewDefinitionInterface $view) + private function addEditMultipleCommand(Contao2BackendViewDefinitionInterface $view): void { + assert($this->container instanceof IMetaModelDataDefinition); $definition = $this->container->getBasicDefinition(); // No actions allowed. Don't add the select command button. if (!$definition->isEditable() && !$definition->isDeletable() && !$definition->isCreatable()) { @@ -186,9 +179,9 @@ private function addEditMultipleCommand(Contao2BackendViewDefinitionInterface $v $commands = $view->getGlobalCommands(); $command = new SelectCommand(); $command - ->setName('all') - ->setLabel('MSC.all.0') - ->setDescription('MSC.all.1'); + ->setName('editAll') + ->setLabel('editAll.label') + ->setDescription('editAll.description'); $parameters = $command->getParameters(); $parameters['act'] = 'select'; @@ -205,8 +198,9 @@ private function addEditMultipleCommand(Contao2BackendViewDefinitionInterface $v * * @return void */ - private function parseModelOperations(Contao2BackendViewDefinitionInterface $view) + private function parseModelOperations(Contao2BackendViewDefinitionInterface $view): void { + assert($this->container instanceof IMetaModelDataDefinition); $collection = $view->getModelCommands(); $scrOffsetAttributes = ['attributes' => 'onclick="Backend.getScrollOffset();"']; @@ -219,15 +213,15 @@ private function parseModelOperations(Contao2BackendViewDefinitionInterface $vie ['act' => 'delete'], 'delete.svg', [ - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $this->translator->trans('MSC.deleteConfirm', [], 'contao_default') - ) + 'attributes' => + 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', ] ); $this->createCommand($collection, 'show', ['act' => 'show'], 'show.svg'); - if ($this->factory->getMetaModel($this->container->getName())->hasVariants()) { + $metaModel = $this->factory->getMetaModel($this->container->getName()); + assert($metaModel instanceof IMetaModel); + if ($metaModel->hasVariants()) { $this->createCommand( $collection, 'createvariant', @@ -238,17 +232,14 @@ private function parseModelOperations(Contao2BackendViewDefinitionInterface $vie // Check if we have some children. foreach ($this->viewCombination->getChildrenOf($this->container->getName()) as $tableName => $screen) { - $metaModel = $this->factory->getMetaModel($tableName); - $caption = $this->getChildModelCaption($metaModel, $screen); - $this->createCommand( $collection, 'edit_' . $tableName, ['table' => $tableName], $this->iconBuilder->getBackendIcon($screen['meta']['backendicon']), [ - 'label' => $caption[0], - 'description' => $caption[1], + 'label' => 'metamodel_edit_as_child.' . $tableName . '.label', + 'description' => 'metamodel_edit_as_child.' . $tableName . '.description', 'idparam' => 'pid' ] ); @@ -256,7 +247,7 @@ private function parseModelOperations(Contao2BackendViewDefinitionInterface $vie } /** - * Build a command into the the command collection. + * Build a command into the command collection. * * @param CommandCollectionInterface $collection The command collection. * @param string $operationName The operation name. @@ -268,10 +259,10 @@ private function parseModelOperations(Contao2BackendViewDefinitionInterface $vie */ private function createCommand( CommandCollectionInterface $collection, - $operationName, - $queryParameters, - $icon, - $extraValues = [] + string $operationName, + array $queryParameters, + string $icon, + array $extraValues = [] ) { $command = $this->getCommandInstance($collection, $operationName); $parameters = $command->getParameters(); @@ -282,14 +273,14 @@ private function createCommand( } if (!$command->getLabel()) { - $command->setLabel($operationName . '.0'); + $command->setLabel($operationName . '.label'); if (isset($extraValues['label'])) { $command->setLabel($extraValues['label']); } } if (!$command->getDescription()) { - $command->setDescription($operationName . '.1'); + $command->setDescription($operationName . '.description'); if (isset($extraValues['description'])) { $command->setDescription($extraValues['description']); } @@ -309,12 +300,11 @@ private function createCommand( * Retrieve or create a command instance of the given name. * * @param CommandCollectionInterface $collection The command collection. - * * @param string $operationName The name of the operation. * * @return CommandInterface */ - private function getCommandInstance(CommandCollectionInterface $collection, $operationName) + private function getCommandInstance(CommandCollectionInterface $collection, string $operationName) { if ($collection->hasCommandNamed($operationName)) { $command = $collection->getCommandNamed($operationName); @@ -338,37 +328,4 @@ private function getCommandInstance(CommandCollectionInterface $collection, $ope return $command; } - - /** - * Create the caption text for the child model. - * - * @param IMetaModel $metaModel The child model. - * @param array $screen The input screen. - * - * @return array - * - * @SuppressWarnings(PHPMD.Superglobals) - * @SuppressWarnings(PHPMD.CamelCaseVariableName) - */ - private function getChildModelCaption($metaModel, $screen) - { - $caption = [ - '', - sprintf( - $GLOBALS['TL_LANG']['MSC']['metamodel_edit_as_child']['label'], - $metaModel->getName() - ) - ]; - - foreach ($screen['label'] as $langCode => $label) { - if (!empty($label) && $langCode === \str_replace('-', '_', $GLOBALS['TL_LANGUAGE'])) { - $caption = [ - $screen['description'][$langCode], - $label - ]; - } - } - - return $caption; - } } diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/ConditionBuilderWithVariants.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/ConditionBuilderWithVariants.php index 43328f95f..df30a3f5e 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/ConditionBuilderWithVariants.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/ConditionBuilderWithVariants.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,20 +12,24 @@ * * @package MetaModels/core * @author Christian Schiffler + * @author Stefan Heimes * @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\CoreBundle\EventListener\DcGeneral\DefinitionBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\ParentChildCondition; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\ParentChildConditionInterface; /** - * This class is the abstract base for the condition builders. + * This class is for the variant model condition builders. + * The variant model is a special form of the hierarchy model. */ class ConditionBuilderWithVariants extends AbstractConditionBuilder { @@ -34,10 +38,10 @@ class ConditionBuilderWithVariants extends AbstractConditionBuilder * * @return void */ - protected function calculate() + protected function calculate(): void { // Basic conditions. - $this->addHierarchicalConditions(); + $this->addVariantConditions(); $this->addParentCondition(); // Conditions for metamodels variants. @@ -46,8 +50,23 @@ protected function calculate() [['property' => 'varbase', 'value' => '1']], $relationship->getSetters() )); + } + + /** + * Parse the correct conditions for a MetaModel with variant support. + * + * @return void + */ + protected function addVariantConditions(): void + { + // Not hierarchical? Get out. + if ($this->container->getBasicDefinition()->getMode() !== BasicDefinitionInterface::MODE_HIERARCHICAL) { + return; + } + + $relationship = $this->getRootCondition(); - $builder = FilterBuilder::fromArrayForRoot((array) $relationship->getFilterArray())->getFilter(); + $builder = FilterBuilder::fromArrayForRoot($relationship->getFilterArray())->getFilter(); $builder->andPropertyEquals('varbase', 1); @@ -59,7 +78,6 @@ protected function calculate() ]; $inverse = []; - /** @var ParentChildConditionInterface $relationship */ $relationship = $this->definition->getChildCondition($this->container->getName(), $this->container->getName()); if ($relationship === null) { @@ -69,8 +87,8 @@ protected function calculate() ->setDestinationName($this->container->getName()); $this->definition->addChildCondition($relationship); } else { - $setter = array_merge_recursive($setter, $relationship->getSetters()); - $inverse = array_merge_recursive($inverse, $relationship->getInverseFilterArray()); + $setter = \array_merge_recursive($setter, $relationship->getSetters()); + $inverse = \array_merge_recursive($inverse, $relationship->getInverseFilterArray()); } $relationship @@ -81,7 +99,7 @@ protected function calculate() ->encapsulateOr() ->andRemotePropertyEquals('vargroup', 'vargroup') ->andRemotePropertyEquals('vargroup', 'id') - ->andRemotePropertyEquals('varbase', 0, true) + ->andRemotePropertyEquals('varbase', '0', true) ->getAllAsArray() ) ->setSetters($setter) diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/ConditionBuilderWithoutVariants.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/ConditionBuilderWithoutVariants.php index d5b3696d4..8d871426f 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/ConditionBuilderWithoutVariants.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/ConditionBuilderWithoutVariants.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. @@ -13,17 +13,23 @@ * @package MetaModels/core * @author Christian Schiffler * @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\CoreBundle\EventListener\DcGeneral\DefinitionBuilder; +use Contao\Input; +use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\FilterBuilder; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\ParentChildCondition; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ModelRelationship\ParentChildConditionInterface; /** - * This class is the abstract base for the condition builders. + * This class is for the hierarchical model condition builders. */ class ConditionBuilderWithoutVariants extends AbstractConditionBuilder { @@ -34,10 +40,10 @@ class ConditionBuilderWithoutVariants extends AbstractConditionBuilder * * @throws \RuntimeException When the conditions can not be determined yet. */ - protected function calculate() + protected function calculate(): void { if ($this->inputScreen['meta']['rendertype'] !== 'standalone') { - if ($this->container->getBasicDefinition()->getMode() == BasicDefinitionInterface::MODE_HIERARCHICAL) { + if ($this->container->getBasicDefinition()->getMode() === BasicDefinitionInterface::MODE_HIERARCHICAL) { throw new \RuntimeException('Hierarchical mode with parent table is not supported yet.'); } } @@ -45,4 +51,65 @@ protected function calculate() $this->addHierarchicalConditions(); $this->addParentCondition(); } + + /** + * Parse the correct conditions for a MetaModel with hierarchical support. + * + * @return void + */ + protected function addHierarchicalConditions(): void + { + // Not hierarchical? Get out. + if ($this->container->getBasicDefinition()->getMode() !== BasicDefinitionInterface::MODE_HIERARCHICAL) { + return; + } + + $relationship = $this->getRootCondition(); + + // NOTE: this might bear problems when the definition will get serialized as the input value will not change. + if (Input::get('pid')) { + $parentValue = ModelId::fromSerialized(Input::get('pid'))->getId(); + } else { + $parentValue = '0'; + } + + if (!$relationship->getSetters()) { + $relationship + ->setSetters([['property' => 'pid', 'value' => $parentValue]]); + } + + $builder = FilterBuilder::fromArrayForRoot($relationship->getFilterArray())->getFilter(); + + $builder->andPropertyEquals('pid', $parentValue); + + $relationship + ->setFilterArray($builder->getAllAsArray()); + + $setter = [['to_field' => 'pid', 'from_field' => 'id']]; + $inverse = []; + + $relationship = $this->definition->getChildCondition($this->container->getName(), $this->container->getName()); + if ($relationship === null) { + $relationship = new ParentChildCondition(); + $relationship + ->setSourceName($this->container->getName()) + ->setDestinationName($this->container->getName()); + $this->definition->addChildCondition($relationship); + } else { + $setter = \array_merge_recursive($setter, $relationship->getSetters()); + $inverse = \array_merge_recursive($inverse, $relationship->getInverseFilterArray()); + } + + // For tl_ prefix, the only unique target can be the id? + // maybe load parent dc and scan for unique in config then. + $relationship + ->setFilterArray( + FilterBuilder::fromArray($relationship->getFilterArray()) + ->getFilter() + ->andRemotePropertyEquals('pid', 'id') + ->getAllAsArray() + ) + ->setSetters($setter) + ->setInverseFilterArray($inverse); + } } diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/Contao2BackendViewDefinitionBuilder.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/Contao2BackendViewDefinitionBuilder.php index 6e3a9d963..b233a41ef 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/Contao2BackendViewDefinitionBuilder.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/Contao2BackendViewDefinitionBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -36,6 +37,10 @@ /** * This class builds the Contao2 backend view definition. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @psalm-suppress PropertyNotSetInConstructor */ class Contao2BackendViewDefinitionBuilder { @@ -46,56 +51,56 @@ class Contao2BackendViewDefinitionBuilder * * @var ViewCombination */ - private $viewCombination; + private ViewCombination $viewCombination; /** * The render setting factory. * * @var IRenderSettingFactory */ - private $renderSettingFactory; + private IRenderSettingFactory $renderSettingFactory; /** * The container being built (only set during build phase). * - * @var IMetaModelDataDefinition + * @var IMetaModelDataDefinition|null */ - private $container; + private IMetaModelDataDefinition|null $container; /** * The backend view definition (only set during build phase). * - * @var Contao2BackendViewDefinitionInterface + * @var Contao2BackendViewDefinitionInterface|null */ - private $definition; + private Contao2BackendViewDefinitionInterface|null $definition; /** * The input screen (only set during build phase). * - * @var array + * @var array|null */ - private $inputScreen; + private array|null $inputScreen; /** * The MetaModel that is the scope (only set during build phase). * - * @var IMetaModel + * @var IMetaModel|null */ - private $metaModel; + private IMetaModel|null $metaModel; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The icon builder. * * @var IconBuilder */ - private $iconBuilder; + private IconBuilder $iconBuilder; /** * Create a new instance. @@ -148,8 +153,9 @@ protected function build(IMetaModelDataDefinition $container) * * @throws DcGeneralInvalidArgumentException When the contained view definition is of invalid type. */ - private function getOrCreateDefinition() + private function getOrCreateDefinition(): Contao2BackendViewDefinitionInterface { + assert($this->container instanceof IMetaModelDataDefinition); if ($this->container->hasDefinition(Contao2BackendViewDefinitionInterface::NAME)) { $view = $this->container->getDefinition(Contao2BackendViewDefinitionInterface::NAME); if (!$view instanceof Contao2BackendViewDefinitionInterface) { @@ -157,8 +163,10 @@ private function getOrCreateDefinition() 'Configured BackendViewDefinition does not implement Contao2BackendViewDefinitionInterface.' ); } + return $view; } + $this->container->setDefinition( Contao2BackendViewDefinitionInterface::NAME, $view = new Contao2BackendViewDefinition() @@ -172,22 +180,31 @@ private function getOrCreateDefinition() * * @return void */ - private function parseListing() + private function parseListing(): void { + assert($this->definition instanceof Contao2BackendViewDefinitionInterface); + assert($this->metaModel instanceof IMetaModel); + if (null === $this->inputScreen) { + return; + } + $listing = $this->definition->getListingConfig(); + if (null === $listing->getRootLabel()) { $listing->setRootLabel($this->metaModel->get('name')); } if (null === $listing->getRootIcon()) { - $listing->setRootIcon($this->iconBuilder->getBackendIcon($this->inputScreen['meta']['backendicon'])); + $listing->setRootIcon( + $this->iconBuilder->getBackendIcon(($this->inputScreen['meta']['backendicon'] ?? '')) + ); } $this->parseListSorting($listing); $this->parseListLabel($listing); - $listing->setShowColumns((bool) $this->inputScreen['meta']['showColumns']); + $listing->setShowColumns((bool) ($this->inputScreen['meta']['showColumns'] ?? false)); } /** @@ -197,24 +214,30 @@ private function parseListing() * * @return void */ - private function parseListSorting(ListingConfigInterface $listing) + private function parseListSorting(ListingConfigInterface $listing): void { if (null === $this->inputScreen) { return; } + $definitions = $listing->getGroupAndSortingDefinition(); foreach ($this->inputScreen['groupSort'] as $information) { + if (empty($information['published'])) { + continue; + } + $definition = $definitions->add(); $definition->setName($information['name']); - if ($information['isdefault'] && !$definitions->hasDefault()) { + if (!empty($information['isdefault']) && !$definitions->hasDefault()) { $definitions->markDefault($definition); } $this->handleSorting($information, $definition); $groupType = $this->convertRenderGroupType($information['rendergrouptype']); - if ($groupType !== GroupAndSortingInformationInterface::GROUP_NONE - && $information['col_name'] + if ( + $groupType !== GroupAndSortingInformationInterface::GROUP_NONE + && !empty($information['col_name']) ) { $propertyInformation = $definition->add(0); $propertyInformation @@ -235,10 +258,10 @@ private function parseListSorting(ListingConfigInterface $listing) * @return void */ private function handleSorting( - $information, + array $information, GroupAndSortingDefinitionInterface $definition - ) { - if ($information['ismanualsort']) { + ): void { + if (!empty($information['ismanualsort'])) { $definition ->add() ->setManualSorting() @@ -246,7 +269,7 @@ private function handleSorting( ->setSortingMode(GroupAndSortingInformationInterface::SORT_ASC); return; } - if ($information['col_name']) { + if (!empty($information['col_name'])) { $definition ->add() ->setProperty($information['col_name']) @@ -261,7 +284,7 @@ private function handleSorting( * * @return string */ - private function convertRenderGroupType($type) + private function convertRenderGroupType(string $type): string { $lookup = [ 'char' => GroupAndSortingInformationInterface::GROUP_CHAR, @@ -272,7 +295,8 @@ private function convertRenderGroupType($type) 'month' => GroupAndSortingInformationInterface::GROUP_MONTH, 'year' => GroupAndSortingInformationInterface::GROUP_YEAR, ]; - if (array_key_exists($type, $lookup)) { + + if (\array_key_exists($type, $lookup)) { return $lookup[$type]; } @@ -286,9 +310,12 @@ private function convertRenderGroupType($type) * * @return void */ - private function parseListLabel(ListingConfigInterface $listing) + private function parseListLabel(ListingConfigInterface $listing): void { + assert($this->container instanceof IMetaModelDataDefinition); + assert($this->metaModel instanceof IMetaModel); $providerName = $this->container->getBasicDefinition()->getDataProvider(); + assert(\is_string($providerName)); if (!$listing->hasLabelFormatter($providerName)) { $formatter = new DefaultModelFormatterConfig(); $listing->setLabelFormatter($providerName, $formatter); @@ -302,9 +329,9 @@ private function parseListLabel(ListingConfigInterface $listing) ); $formatter->setPropertyNames( - array_merge($formatter->getPropertyNames(), $renderSetting->getSettingNames()) + \array_merge($formatter->getPropertyNames(), $renderSetting->getSettingNames()) ); - $formatter->setFormat(str_repeat('%s ', count($formatter->getPropertyNames()))); + $formatter->setFormat(\str_repeat('%s ', \count($formatter->getPropertyNames()))); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/DataProviderBuilder.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/DataProviderBuilder.php index fa3923742..8d819725a 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/DataProviderBuilder.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/DataProviderBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -41,14 +42,14 @@ class DataProviderBuilder * * @var ViewCombination */ - private $viewCombination; + private ViewCombination $viewCombination; /** * The factory to use. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Create a new instance. @@ -72,7 +73,7 @@ public function __construct(ViewCombination $viewCombination, IFactory $factory) protected function build(IMetaModelDataDefinition $container) { $inputScreen = $this->viewCombination->getScreen($container->getName()); - if (!$inputScreen) { + if (null === $inputScreen) { return; } $meta = $inputScreen['meta']; diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/MetaModelDefinitionBuilder.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/MetaModelDefinitionBuilder.php index ec2285b3f..3a3c890dc 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/MetaModelDefinitionBuilder.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/MetaModelDefinitionBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,7 +38,7 @@ class MetaModelDefinitionBuilder * * @var ViewCombination */ - private $viewCombination; + private ViewCombination $viewCombination; /** * Create a new instance. @@ -60,7 +61,7 @@ protected function build(IMetaModelDataDefinition $container) { $definition = $this->createOrGetDefinition($container); - if (empty($combination = $this->viewCombination->getCombination($container->getName()))) { + if (null === ($combination = $this->viewCombination->getCombination($container->getName()))) { return; } diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PaletteBuilder.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PaletteBuilder.php index 08607124d..5c1593083 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PaletteBuilder.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PaletteBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +23,7 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\ConditionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefaultPalettesDefinition; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PalettesDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\DefaultPaletteCondition; @@ -40,6 +41,8 @@ /** * This class takes care of the palette building. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PaletteBuilder { @@ -50,21 +53,21 @@ class PaletteBuilder * * @var ViewCombination */ - private $viewCombination; + private ViewCombination $viewCombination; /** * The factory to use. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The property condition factory. * * @var PropertyConditionFactory */ - private $conditionFactory; + private PropertyConditionFactory $conditionFactory; /** * Create a new instance. @@ -95,7 +98,10 @@ protected function build(IMetaModelDataDefinition $container) if (null === ($inputScreen = $this->viewCombination->getScreen($container->getName()))) { return; } - $metaModel = $this->factory->getMetaModel($container->getName()); + + $metaModel = $this->factory->getMetaModel($container->getName()); + assert($metaModel instanceof IMetaModel); + $variantHandling = $metaModel->hasVariants(); $palettesDefinition = $this->getOrCreatePaletteDefinition($container); @@ -106,12 +112,13 @@ protected function build(IMetaModelDataDefinition $container) ->setName('default') ->setCondition(new DefaultPaletteCondition()); + $prefix = 'inputscreen.' . $inputScreen['meta']['id'] . '.'; foreach ($inputScreen['legends'] as $legendName => $legendInfo) { - $legend = new Legend($legendName); + $legend = new Legend($prefix . $legendName); $legend->setInitialVisibility(!$legendInfo['hide']); $palette->addLegend($legend); - $legendConditions = $this->buildCondition($legendInfo['condition'], $metaModel); + $legendConditions = $this->buildCondition(($legendInfo['condition'] ?? null), $metaModel); foreach ($legendInfo['properties'] as $property) { $legend->addProperty( $this->createProperty( @@ -131,13 +138,17 @@ protected function build(IMetaModelDataDefinition $container) * * @param IMetaModelDataDefinition $container The container. * - * @return DefaultPalettesDefinition|\ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DefinitionInterface + * @return PalettesDefinitionInterface */ - private function getOrCreatePaletteDefinition(IMetaModelDataDefinition $container) + private function getOrCreatePaletteDefinition(IMetaModelDataDefinition $container): PalettesDefinitionInterface { if ($container->hasDefinition(PalettesDefinitionInterface::NAME)) { - return $container->getDefinition(PalettesDefinitionInterface::NAME); + $definition = $container->getDefinition(PalettesDefinitionInterface::NAME); + assert($definition instanceof PalettesDefinitionInterface); + + return $definition; } + $container->setDefinition( PalettesDefinitionInterface::NAME, $palettesDefinition = new DefaultPalettesDefinition() @@ -181,10 +192,7 @@ private function createProperty( $chain = new PropertyConditionChain(); $paletteProperty->setVisibleCondition($chain); $chain->addCondition( - new BooleanCondition( - !((isset($extra['doNotShow']) && $extra['doNotShow']) - || (isset($extra['hideInput']) && $extra['hideInput'])) - ) + new BooleanCondition(!(bool) ($extra['doNotShow'] ?? false) || !(bool) ($extra['hideInput'] ?? false)) ); if (null !== $condition) { @@ -201,14 +209,13 @@ private function createProperty( * Build the conditions for the passed condition array. * * @param array|null $condition The condition information. - * * @param IMetaModel $metaModel The MetaModel instance. * * @return null|ConditionInterface * * @throws \RuntimeException Throws if condition type not be transformed to an instance. */ - private function buildCondition($condition, $metaModel) + private function buildCondition(?array $condition, IMetaModel $metaModel): ?ConditionInterface { if (null === $condition) { return null; diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PanelBuilder.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PanelBuilder.php index ddeca449b..6666361d6 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PanelBuilder.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PanelBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -36,6 +37,8 @@ /** * This class handles the panel building. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PanelBuilder { @@ -46,14 +49,14 @@ class PanelBuilder * * @var ViewCombination */ - private $viewCombination; + private ViewCombination $viewCombination; /** * The input screen to use (only set during build phase). * - * @var array + * @var array|null */ - private $inputScreen; + private ?array $inputScreen = null; /** * Create a new instance. @@ -85,10 +88,10 @@ protected function build(IMetaModelDataDefinition $container) } // Get the panel layout. - $panelLayout = $this->inputScreen['meta']['panelLayout']; + $panelLayout = $this->inputScreen['meta']['panelLayout'] ?? null; // Check if we have a layout. - if (empty($panelLayout)) { + if (null === $panelLayout) { return; } @@ -101,7 +104,7 @@ protected function build(IMetaModelDataDefinition $container) foreach ($arrRows as $rowNo => $rowElements) { // Get the row, if we have one or create a new one. - if ($panelRows->getRowCount() < ($rowNo + 1)) { + if ($panelRows->getRowCount() < ((int) $rowNo + 1)) { $panelRow = $panelRows->addRow(); } else { $panelRow = $panelRows->getRow($rowNo); @@ -109,18 +112,18 @@ protected function build(IMetaModelDataDefinition $container) // Get the fields. $fields = StringUtil::trimsplit(',', $rowElements); - $fields = array_reverse($fields); + $fields = \array_reverse($fields); $this->parsePanelRow($fields, $panelRow); // If we have no entries for this row, remove it. - if ($panelRow->getCount() == 0) { + if ($panelRow->getCount() === 0) { $panelRows->deleteRow($rowNo); } } $this->ensureSubmitElement($panelRows); - $this->inputScreen = null; + $this->inputScreen = []; } /** @@ -132,7 +135,7 @@ protected function build(IMetaModelDataDefinition $container) * * @return void */ - private function ensureSubmitElement($panelRows) + private function ensureSubmitElement(PanelRowCollectionInterface $panelRows): void { // Check if we have a submit button. $hasSubmit = false; @@ -160,12 +163,11 @@ private function ensureSubmitElement($panelRows) * Parse a single row with all elements. * * @param array $fields A list of fields for adding to the row. - * * @param PanelRowInterface $panelRow The row container itself. * * @return void */ - private function parsePanelRow($fields, PanelRowInterface $panelRow) + private function parsePanelRow(array $fields, PanelRowInterface $panelRow): void { // Parse each type. foreach ($fields as $field) { @@ -203,9 +205,9 @@ private function parsePanelRow($fields, PanelRowInterface $panelRow) * * @return void */ - private function parsePanelFilter(PanelRowInterface $row) + private function parsePanelFilter(PanelRowInterface $row): void { - foreach ($this->inputScreen['properties'] as $value) { + foreach ($this->inputScreen['properties'] ?? [] as $value) { if (!empty($value['filter'])) { $element = new DefaultFilterElementInformation(); $element->setPropertyName($value['col_name']); @@ -223,7 +225,7 @@ private function parsePanelFilter(PanelRowInterface $row) * * @return void */ - private function parsePanelSort(PanelRowInterface $row) + private function parsePanelSort(PanelRowInterface $row): void { if (!$row->hasElement('sort')) { $element = new DefaultSortElementInformation(); @@ -240,7 +242,7 @@ private function parsePanelSort(PanelRowInterface $row) * * @throws \InvalidArgumentException When the search element does not implement the correct interface. */ - private function parsePanelSearch(PanelRowInterface $row) + private function parsePanelSearch(PanelRowInterface $row): void { if ($row->hasElement('search')) { $element = $row->getElement('search'); @@ -252,7 +254,7 @@ private function parsePanelSearch(PanelRowInterface $row) throw new \InvalidArgumentException('Search element does not implement the correct interface.'); } - foreach ($this->inputScreen['properties'] as $value) { + foreach ($this->inputScreen['properties'] ?? [] as $value) { if (!empty($value['search'])) { $element->addProperty($value['col_name']); } @@ -270,7 +272,7 @@ private function parsePanelSearch(PanelRowInterface $row) * * @return void */ - private function parsePanelLimit(PanelRowInterface $row) + private function parsePanelLimit(PanelRowInterface $row): void { if (!$row->hasElement('limit')) { $row->addElement(new DefaultLimitElementInformation()); @@ -284,7 +286,7 @@ private function parsePanelLimit(PanelRowInterface $row) * * @return void */ - private function parsePanelSubmit(PanelRowInterface $row) + private function parsePanelSubmit(PanelRowInterface $row): void { if (!$row->hasElement('submit')) { $row->addElement(new DefaultSubmitElementInformation()); diff --git a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PropertyDefinitionBuilder.php b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PropertyDefinitionBuilder.php index 972fa7f3f..e18983089 100644 --- a/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PropertyDefinitionBuilder.php +++ b/src/CoreBundle/EventListener/DcGeneral/DefinitionBuilder/PropertyDefinitionBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2021 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,11 +31,14 @@ use MetaModels\DcGeneral\DataDefinition\IMetaModelDataDefinition; use MetaModels\DcGeneral\Events\MetaModel\BuildAttributeEvent; use MetaModels\IFactory; +use MetaModels\IMetaModel; use MetaModels\ViewCombination\ViewCombination; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This class builds the property information. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class PropertyDefinitionBuilder { @@ -45,21 +49,21 @@ class PropertyDefinitionBuilder * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * The view combinations. * * @var ViewCombination */ - private $viewCombination; + private ViewCombination $viewCombination; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Create a new instance. @@ -88,7 +92,7 @@ public function __construct( protected function build(IMetaModelDataDefinition $container) { $inputScreen = $this->viewCombination->getScreen($container->getName()); - if (!$inputScreen) { + if (null === $inputScreen) { return; } @@ -100,6 +104,7 @@ protected function build(IMetaModelDataDefinition $container) } $metaModel = $this->factory->getMetaModel($container->getName()); + assert($metaModel instanceof IMetaModel); // If the current metamodel has variants add the varbase and vargroup to the definition. if ($metaModel->hasVariants()) { @@ -140,15 +145,14 @@ protected function build(IMetaModelDataDefinition $container) * * @return void */ - private function buildProperty(PropertiesDefinitionInterface $definition, IAttribute $attribute, array $propInfo) - { - if (!$attribute) { - return; - } - + private function buildProperty( + PropertiesDefinitionInterface $definition, + IAttribute $attribute, + array $propInfo + ): void { $property = $this->getOrCreateProperty($definition, $attribute->getColName()); - $this->setLabel($property, $propInfo, $attribute); - $this->setDescription($property, $propInfo); + $this->setLabel($property); + $this->setDescription($property); $this->setDefaultValue($property, $propInfo); $this->setExcluded($property, $propInfo); $this->setSearchable($property, $propInfo); @@ -168,11 +172,12 @@ private function buildProperty(PropertiesDefinitionInterface $definition, IAttri * * @return PropertyInterface */ - private function getOrCreateProperty(PropertiesDefinitionInterface $definition, $propName) + private function getOrCreateProperty(PropertiesDefinitionInterface $definition, string $propName): PropertyInterface { if ($definition->hasProperty($propName)) { return $definition->getProperty($propName); } + $property = new DefaultProperty($propName); $definition->addProperty($property); @@ -183,44 +188,30 @@ private function getOrCreateProperty(PropertiesDefinitionInterface $definition, * Set the label in the property. * * @param PropertyInterface $property The property definition. - * @param array $propInfo The property info array. - * @param IAttribute $attribute The attribute. * * @return void */ - private function setLabel(PropertyInterface $property, $propInfo, IAttribute $attribute) + private function setLabel(PropertyInterface $property): void { if ($property->getLabel()) { return; } - if (!isset($propInfo['label'])) { - $property->setLabel($attribute->getName()); - return; - } - $lang = $propInfo['label']; - if (is_array($lang)) { - $property->setLabel(reset($lang)); - $property->setDescription(next($lang)); - return; - } - $property->setLabel($lang); + $property->setLabel($property->getName() . '.label'); } /** * Set the description in the property. * * @param PropertyInterface $property The property definition. - * @param array $propInfo The property info array. * * @return void */ - private function setDescription(PropertyInterface $property, $propInfo) + private function setDescription(PropertyInterface $property): void { - if ($property->getDescription() || !isset($propInfo['description'])) { + if ($property->getDescription()) { return; } - - $property->setDescription($propInfo['description']); + $property->setDescription($property->getName() . '.description'); } /** @@ -231,7 +222,7 @@ private function setDescription(PropertyInterface $property, $propInfo) * * @return void */ - private function setDefaultValue(PropertyInterface $property, $propInfo) + private function setDefaultValue(PropertyInterface $property, array $propInfo): void { if (!isset($propInfo['default'])) { return; @@ -247,7 +238,7 @@ private function setDefaultValue(PropertyInterface $property, $propInfo) * * @return void */ - private function setExcluded(PropertyInterface $property, $propInfo) + private function setExcluded(PropertyInterface $property, array $propInfo): void { if (!isset($propInfo['exclude'])) { return; @@ -263,7 +254,7 @@ private function setExcluded(PropertyInterface $property, $propInfo) * * @return void */ - private function setSearchable(PropertyInterface $property, $propInfo) + private function setSearchable(PropertyInterface $property, array $propInfo): void { if (!isset($propInfo['search'])) { return; @@ -279,7 +270,7 @@ private function setSearchable(PropertyInterface $property, $propInfo) * * @return void */ - private function setFilterable(PropertyInterface $property, $propInfo) + private function setFilterable(PropertyInterface $property, array $propInfo): void { if (!isset($propInfo['filter'])) { return; @@ -295,9 +286,9 @@ private function setFilterable(PropertyInterface $property, $propInfo) * * @return void */ - private function setWidgetType(PropertyInterface $property, $propInfo) + private function setWidgetType(PropertyInterface $property, array $propInfo): void { - if (null !== $property->getWidgetType() || !isset($propInfo['inputType'])) { + if ('' !== ($property->getWidgetType()) || !isset($propInfo['inputType'])) { return; } @@ -312,7 +303,7 @@ private function setWidgetType(PropertyInterface $property, $propInfo) * * @return void */ - private function setOptions(PropertyInterface $property, $propInfo) + private function setOptions(PropertyInterface $property, array $propInfo): void { if (null !== $property->getOptions() || !isset($propInfo['options'])) { return; @@ -329,7 +320,7 @@ private function setOptions(PropertyInterface $property, $propInfo) * * @return void */ - private function setExplanation(PropertyInterface $property, $propInfo) + private function setExplanation(PropertyInterface $property, array $propInfo): void { if ($property->getExplanation() || !isset($propInfo['explanation'])) { return; @@ -347,14 +338,14 @@ private function setExplanation(PropertyInterface $property, $propInfo) * * @return void */ - private function setEval($property, $propInfo, $isTranslated) + private function setEval(PropertyInterface $property, array $propInfo, bool $isTranslated): void { - $extra = isset($propInfo['eval']) ? $propInfo['eval'] : []; + $extra = $propInfo['eval'] ?? []; if ($isTranslated) { $extra['tl_class'] = 'translat-attr' . (!empty($extra['tl_class']) ? ' ' . $extra['tl_class'] : ''); } - $property->setExtra(array_merge((array) $property->getExtra(), $extra)); + $property->setExtra(\array_merge($property->getExtra(), $extra)); } /** @@ -365,9 +356,9 @@ private function setEval($property, $propInfo, $isTranslated) * * @return void */ - private function setEmptyValue(PropertyInterface $property, array $propInfo) + private function setEmptyValue(PropertyInterface $property, array $propInfo): void { - if (!array_key_exists('empty_value', $propInfo) || !($property instanceof EmptyValueAwarePropertyInterface)) { + if (!\array_key_exists('empty_value', $propInfo) || !($property instanceof EmptyValueAwarePropertyInterface)) { return; } $property->setEmptyValue($propInfo['empty_value']); diff --git a/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/AssetPopulator.php b/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/AssetPopulator.php index e9ee94d16..521b311e5 100644 --- a/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/AssetPopulator.php +++ b/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/AssetPopulator.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2022 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,13 +13,16 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2022 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\EnvironmentPopulator; +use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; + /** * This class adds the style sheet. */ @@ -30,12 +33,15 @@ class AssetPopulator /** * Populate the environment. * + * @param EnvironmentInterface $environment The environment. + * * @return void * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) + * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function populate() + public function populate(EnvironmentInterface $environment) { $GLOBALS['TL_CSS'][] = 'bundles/metamodelscore/css/style.css'; } diff --git a/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/AttributePopulator.php b/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/AttributePopulator.php index ad86e0023..abd9a7686 100644 --- a/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/AttributePopulator.php +++ b/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/AttributePopulator.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 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. @@ -13,16 +13,19 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2021 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\CoreBundle\EventListener\DcGeneral\EnvironmentPopulator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use MetaModels\DcGeneral\Events\MetaModel\PopulateAttributeEvent; use MetaModels\IFactory; +use MetaModels\IMetaModel; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -37,14 +40,14 @@ class AttributePopulator * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Create a new instance. @@ -67,7 +70,10 @@ public function __construct(EventDispatcherInterface $dispatcher, IFactory $fact */ public function populate(EnvironmentInterface $environment) { - $metaModel = $this->factory->getMetaModel($environment->getDataDefinition()->getName()); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $metaModel = $this->factory->getMetaModel($dataDefinition->getName()); + assert($metaModel instanceof IMetaModel); foreach ($metaModel->getAttributes() as $attribute) { $event = new PopulateAttributeEvent($metaModel, $attribute, $environment); // Trigger BuildAttribute Event. diff --git a/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/DataProviderPopulator.php b/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/DataProviderPopulator.php index 5007852ec..c9e80a2d5 100644 --- a/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/DataProviderPopulator.php +++ b/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/DataProviderPopulator.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. @@ -13,13 +13,15 @@ * @package MetaModels/core * @author Christian Schiffler * @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\CoreBundle\EventListener\DcGeneral\EnvironmentPopulator; +use ContaoCommunityAlliance\DcGeneral\Contao\Dca\ContaoDataProviderInformation; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\DataProviderDefinitionInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; @@ -40,21 +42,21 @@ class DataProviderPopulator * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The event dispatcher to pass to drivers. * - * @var EventDispatcherInterface + * @var EventDispatcherInterface|null */ - private $dispatcher = null; + private ?EventDispatcherInterface $dispatcher = null; /** * The database connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * Create a new instance. @@ -79,11 +81,13 @@ public function __construct(IFactory $factory, EventDispatcherInterface $dispatc */ public function populate(EnvironmentInterface $environment) { - foreach ([ - $environment->getDataDefinition(), - $environment->getParentDataDefinition(), - $environment->getRootDataDefinition() - ] as $definition) { + foreach ( + [ + $environment->getDataDefinition(), + $environment->getParentDataDefinition(), + $environment->getRootDataDefinition() + ] as $definition + ) { if (!$definition instanceof ContainerInterface) { continue; } @@ -103,11 +107,11 @@ private function injectServiceContainerIntoDataDrivers($providerDefinitions, $en { foreach ($providerDefinitions as $provider) { $providerInstance = $environment->getDataProvider($provider->getName()); - if ($providerInstance instanceof Driver) { + if ($providerInstance instanceof Driver && $provider instanceof ContaoDataProviderInformation) { $initialization = $provider->getInitializationData(); $metaModel = $this->factory->getMetaModel($initialization['source']); $providerInstance->setBaseConfig( - array_merge($initialization, ['metaModel' => $metaModel]) + \array_merge($initialization, ['metaModel' => $metaModel]) ); $providerInstance->setDispatcher($this->dispatcher); $providerInstance->setConnection($this->connection); diff --git a/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/MetaModelPopulatorTrait.php b/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/MetaModelPopulatorTrait.php index df78c1361..fd395f086 100644 --- a/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/MetaModelPopulatorTrait.php +++ b/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/MetaModelPopulatorTrait.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2022 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2022 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ diff --git a/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/TranslatorPopulator.php b/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/TranslatorPopulator.php index 7ff13bdbf..d25e151b7 100644 --- a/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/TranslatorPopulator.php +++ b/src/CoreBundle/EventListener/DcGeneral/EnvironmentPopulator/TranslatorPopulator.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,19 +14,23 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\EnvironmentPopulator; -use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; -use ContaoCommunityAlliance\Contao\Bindings\Events\System\LoadLanguageFileEvent; +use Contao\System; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\Translator\StaticTranslator; +use ContaoCommunityAlliance\Translator\SymfonyTranslatorBridge; use ContaoCommunityAlliance\Translator\TranslatorChain; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use MetaModels\Helper\LocaleUtil; use MetaModels\ViewCombination\ViewCombination; +use Symfony\Contracts\Translation\TranslatorInterface as SymfonyTranslator; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -36,30 +40,18 @@ class TranslatorPopulator { use MetaModelPopulatorTrait; - /** - * The event dispatcher. - * - * @var EventDispatcherInterface - */ - private $dispatcher; - - /** - * The view combination. - * - * @var ViewCombination - */ - private $viewCombination; - /** * Create a new instance. * * @param EventDispatcherInterface $dispatcher The event dispatcher. * @param ViewCombination $viewCombination The view combination. + * @param SymfonyTranslator $translator The translator. */ - public function __construct(EventDispatcherInterface $dispatcher, ViewCombination $viewCombination) - { - $this->dispatcher = $dispatcher; - $this->viewCombination = $viewCombination; + public function __construct( + private EventDispatcherInterface $dispatcher, + private ViewCombination $viewCombination, + private SymfonyTranslator $translator + ) { } /** @@ -75,6 +67,8 @@ public function __construct(EventDispatcherInterface $dispatcher, ViewCombinatio protected function populate(EnvironmentInterface $environment) { $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + if (!($translator instanceof TranslatorChain)) { $translatorChain = new TranslatorChain(); $translatorChain->add($translator); @@ -82,20 +76,12 @@ protected function populate(EnvironmentInterface $environment) } else { $translatorChain = $translator; } + $translatorChain->add(new SymfonyTranslatorBridge($this->translator)); $translatorChain->add($translator = new StaticTranslator()); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $definitionName = $dataDefinition->getName(); - // Map the tl_metamodel_item domain over to this domain. - $this->dispatcher->dispatch( - new LoadLanguageFileEvent('tl_metamodel_item'), - ContaoEvents::SYSTEM_LOAD_LANGUAGE_FILE - ); - - $definitionName = $environment->getDataDefinition()->getName(); - $this->mapTranslations( - $GLOBALS['TL_LANG']['tl_metamodel_item'], - $definitionName, - $translator - ); if (null === $inputScreen = $this->viewCombination->getScreen($definitionName)) { return; } @@ -121,7 +107,7 @@ private function mapTranslations($array, $domain, StaticTranslator $translator, { foreach ($array as $key => $value) { $newKey = ($baseKey ? $baseKey . '.' : '') . $key; - if (is_array($value)) { + if (\is_array($value)) { $this->mapTranslations($value, $domain, $translator, $newKey); } else { $translator->setValue($newKey, $value, $domain); @@ -144,10 +130,11 @@ private function mapTranslations($array, $domain, StaticTranslator $translator, private function addInputScreenTranslations(StaticTranslator $translator, $inputScreen, $containerName) { // Either 2 or 5 char long language code. - $currentLocale = \str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + $currentLocale = LocaleUtil::formatAsLocale($GLOBALS['TL_LANGUAGE']); // Either 2 char language code or null. - $shortLocale = (false !== strpos($currentLocale, '_')) - ? explode('_', $currentLocale, 2)[0] + $shortLocale = (\str_contains($currentLocale, '_')) + ? \explode('_', $currentLocale, 2)[0] : null; foreach ($inputScreen['legends'] as $legendName => $legendInfo) { @@ -159,21 +146,24 @@ private function addInputScreenTranslations(StaticTranslator $translator, $input ); $fallbackLocales = [$currentLocale]; - if ($shortLocale && !in_array($currentLocale, array_keys($legendInfo['label']), true)) { + if ((null !== $shortLocale) && !\array_key_exists($currentLocale, $legendInfo['label'])) { $fallbackLocales[] = $shortLocale; } + foreach ($legendInfo['label'] as $langCode => $label) { // Default is already handled above, do not overwrite! if ($langCode === 'default') { continue; } + $translator->setValue( $legendName . '_legend', $label, $containerName, $langCode ); - if (in_array($langCode, $fallbackLocales)) { + + if (\in_array($langCode, $fallbackLocales)) { $translator->setValue( $legendName . '_legend', $label, diff --git a/src/CoreBundle/EventListener/DcGeneral/ItemRendererListener.php b/src/CoreBundle/EventListener/DcGeneral/ItemRendererListener.php index 001a3c144..67ed36340 100644 --- a/src/CoreBundle/EventListener/DcGeneral/ItemRendererListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/ItemRendererListener.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. @@ -15,7 +15,7 @@ * @author David Molineus * @author Alexander Menk * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -46,7 +46,7 @@ class ItemRendererListener * * @var IRenderSettingFactory */ - private $renderSettingFactory; + private IRenderSettingFactory $renderSettingFactory; /** * Create a new instance. @@ -82,35 +82,33 @@ public function render(ModelToLabelEvent $event) } $nativeItem = $model->getItem(); - $metaModel = $nativeItem->getMetaModel(); + assert($nativeItem instanceof IItem); + $metaModel = $nativeItem->getMetaModel(); $renderSetting = $this->renderSettingFactory ->createCollection($metaModel, $definition->getMetaModelDefinition()->getActiveRenderSetting()); - if (!$renderSetting) { - return; - } - - $data = array($nativeItem->parseValue('html5', $renderSetting)); + $data = [$nativeItem->parseValue('html5', $renderSetting)]; if ($listing->getShowColumns()) { $event->setArgs($data[0]['html5']); return; } - $template = new Template($renderSetting->get('template')); + $template = new Template($renderSetting->get('template') ?? ''); $renderSetting = self::removeInvariantAttributes($nativeItem, $renderSetting); $template->setData( - array( + [ 'settings' => $renderSetting, - 'items' => new Items(array($nativeItem)), + 'items' => new Items([$nativeItem]), 'view' => $renderSetting, 'data' => $data - ) + ] ); - $event->setLabel('%s')->setArgs(array($template->parse('html5'))); + /** @psalm-suppress InvalidArgument */ + $event->setLabel('%s')->setArgs([$template->parse('html5')]); } /** @@ -134,8 +132,10 @@ public function getReadableValue(RenderReadablePropertyValueEvent $event) } $nativeItem = $model->getItem(); - $metaModel = $nativeItem->getMetaModel(); - $propName = $event->getProperty()->getName(); + assert($nativeItem instanceof IItem); + $metaModel = $nativeItem->getMetaModel(); + $propName = $event->getProperty()->getName(); + if ($nativeItem->getAttribute($propName) instanceof IInternal) { return; } @@ -145,23 +145,20 @@ public function getReadableValue(RenderReadablePropertyValueEvent $event) $definition->getMetaModelDefinition()->getActiveRenderSetting() ); - if (!$renderSetting) { - return; - } - $result = $nativeItem->parseAttribute($propName, 'text', $renderSetting); if (!isset($result['text'])) { // If hide empty values active and the value IS empty, this is expected. See #1318. - if ($renderSetting->get('hideEmptyValues') && EmptyTest::isEmptyValue($nativeItem->get($propName))) { + if ((bool) $renderSetting->get('hideEmptyValues') && EmptyTest::isEmptyValue($nativeItem->get($propName))) { return; } $event->setRendered( - sprintf( + \sprintf( 'Unexpected behaviour, attribute %s text representation was not rendered.', $event->getProperty()->getName() ) ); + return; } @@ -182,26 +179,32 @@ public function addAdditionalParentHeaderFields(GetParentHeaderEvent $event) if (!$parentModel instanceof Model) { return; } + $environment = $event->getEnvironment(); /** @var IMetaModelDataDefinition $definition */ $definition = $environment->getDataDefinition(); $item = $parentModel->getItem(); + assert($item instanceof IItem); $metaModel = $item->getMetaModel(); $renderSetting = $this->renderSettingFactory->createCollection( $metaModel, $definition->getMetaModelDefinition()->getActiveRenderSetting() ); - $additional = array(); + $additional = []; foreach ($renderSetting->getSettingNames() as $name) { $parsed = $item->parseAttribute($name, 'text', $renderSetting); - $name = $item->getAttribute($name)->getName(); + $attribute = $item->getAttribute($name); + if (null === $attribute) { + continue; + } + $name = $attribute->getName(); $additional[$name] = $parsed['text']; } - $additional = array_merge( + $additional = \array_merge( $additional, $event->getAdditional() ); @@ -215,7 +218,6 @@ public function addAdditionalParentHeaderFields(GetParentHeaderEvent $event) * This is done by cloning the input collection of render settings and removing any invariant attribute. * * @param IItem $nativeItem The native item. - * * @param ICollection $renderSetting The render setting to be used. * * @return ICollection @@ -229,7 +231,7 @@ private function removeInvariantAttributes(IItem $nativeItem, ICollection $rende $renderSetting = clone $renderSetting; // Loop over all attributes and remove those from rendering that are not desired. - foreach (array_keys($model->getInVariantAttributes()) as $strAttrName) { + foreach (\array_keys($model->getInVariantAttributes()) as $strAttrName) { $renderSetting->setSetting($strAttrName, null); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/MetaModel/CreateVariantButtonListener.php b/src/CoreBundle/EventListener/DcGeneral/MetaModel/CreateVariantButtonListener.php index 1cf862ce4..2edd8d09b 100644 --- a/src/CoreBundle/EventListener/DcGeneral/MetaModel/CreateVariantButtonListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/MetaModel/CreateVariantButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author Christopher Boelter * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2012-2021 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -29,17 +30,26 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\EditMask; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetBreadcrumbEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\ActionEvent; use ContaoCommunityAlliance\DcGeneral\Event\PostCreateModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PreCreateModelEvent; use ContaoCommunityAlliance\DcGeneral\Event\PreEditModelEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use MetaModels\DcGeneral\Data\Driver; use MetaModels\DcGeneral\Data\Model; use MetaModels\IFactory; +use MetaModels\IItem; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Event handler class to manage the "create variant" button. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CreateVariantButtonListener { @@ -48,20 +58,19 @@ class CreateVariantButtonListener * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The request scope determinator. * * @var RequestScopeDeterminator */ - private $scopeMatcher; + private RequestScopeDeterminator $scopeMatcher; /** * Create a new instance. * * @param IFactory $factory The factory. - * * @param RequestScopeDeterminator $scopeMatcher The request scope determinator. */ public function __construct(IFactory $factory, RequestScopeDeterminator $scopeMatcher) @@ -79,12 +88,18 @@ public function __construct(IFactory $factory, RequestScopeDeterminator $scopeMa */ public function createButton(GetOperationButtonEvent $event) { - if ('createvariant' !== $event->getCommand()->getName()) { + $command = $event->getCommand(); + assert($command instanceof CommandInterface); + + if ('createvariant' !== $command->getName()) { return; } + /** @var Model $model */ - $model = $event->getModel(); - $metamodel = $model->getItem()->getMetaModel(); + $model = $event->getModel(); + $item = $model->getItem(); + assert($item instanceof IItem); + $metamodel = $item->getMetaModel(); if (!$metamodel->hasVariants() || $model->getProperty('varbase') === '0') { $event->setHtml(''); @@ -103,20 +118,27 @@ public function createButton(GetOperationButtonEvent $event) */ public function handleCreateVariantAction(ActionEvent $event) { - if (false === $this->scopeMatcher->currentScopeIsBackend() - || 'createvariant' !== $event->getAction()->getName()) { + if ( + false === $this->scopeMatcher->currentScopeIsBackend() + || 'createvariant' !== $event->getAction()->getName() + ) { return; } - $environment = $event->getEnvironment(); - $view = $environment->getView(); - $dataProvider = $environment->getDataProvider(); + $environment = $event->getEnvironment(); + $view = $environment->getView(); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof Driver); $inputProvider = $environment->getInputProvider(); - $modelId = $inputProvider->hasParameter('id') + assert($inputProvider instanceof InputProviderInterface); + $modelId = $inputProvider->hasParameter('id') ? ModelId::fromSerialized($inputProvider->getParameter('id')) : null; - /** @var \MetaModels\DcGeneral\Data\Driver $dataProvider */ + if (null === $modelId) { + throw new \RuntimeException('No model id passed.'); + } + $model = $dataProvider ->createVariant( $dataProvider @@ -124,35 +146,41 @@ public function handleCreateVariantAction(ActionEvent $event) ->setId($modelId->getId()) ); - if ($model == null) { + if ($model === null) { throw new \RuntimeException(sprintf( 'Could not find model with id %s for creating a variant.', - $modelId + $modelId->getSerialized() )); } $metaModel = $this->factory->getMetaModel($model->getProviderName()); - if (!$metaModel || !$metaModel->hasVariants()) { + if ((null === $metaModel) || !$metaModel->hasVariants()) { return; } - $preFunction = function ($environment, $model) { - /** @var EnvironmentInterface $environment */ + $preFunction = function (EnvironmentInterface $environment, ModelInterface $model): void { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $copyEvent = new PreCreateModelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($copyEvent, $copyEvent::NAME); + $dispatcher->dispatch($copyEvent, $copyEvent::NAME); }; - $postFunction = function ($environment, $model) { - /** @var EnvironmentInterface $environment */ + $postFunction = function (EnvironmentInterface $environment, ModelInterface $model): void { + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $copyEvent = new PostCreateModelEvent($environment, $model); - $environment->getEventDispatcher()->dispatch($copyEvent::NAME, $copyEvent); + $dispatcher->dispatch($copyEvent, $copyEvent::NAME); }; if (!$view instanceof BackendViewInterface) { throw new \InvalidArgumentException('Invalid view registered in environment.'); } - $editMask = new EditMask($view, $model, null, $preFunction, $postFunction, $this->breadcrumb($environment)); + $newModel = clone $model; + $editMask = + new EditMask($view, $newModel, $model, $preFunction, $postFunction, $this->breadcrumb($environment)); $event->setResponse($editMask->execute()); } @@ -175,7 +203,8 @@ public function presetVariantBase(PreEditModelEvent $event) // Get the item and check the context. $nativeItem = $model->getItem(); - $metaModel = $nativeItem->getMetaModel(); + assert($nativeItem instanceof IItem); + $metaModel = $nativeItem->getMetaModel(); if ($metaModel->hasVariants() && (!$nativeItem->get('vargroup'))) { $nativeItem->set('varbase', '1'); @@ -194,20 +223,21 @@ public function presetVariantBase(PreEditModelEvent $event) */ protected function breadcrumb(EnvironmentInterface $environment) { - $event = new GetBreadcrumbEvent($environment); - - $environment->getEventDispatcher()->dispatch($event, $event::NAME); + $event = new GetBreadcrumbEvent($environment); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, $event::NAME); $arrReturn = $event->getElements(); - if (!is_array($arrReturn) || count($arrReturn) == 0) { - return null; + if ([] === $arrReturn) { + return ''; } $GLOBALS['TL_CSS'][] = 'bundles/ccadcgeneral/css/generalBreadcrumb.css'; $objTemplate = new ContaoBackendViewTemplate('dcbe_general_breadcrumb'); - $objTemplate->elements = $arrReturn; + $objTemplate->set('elements', $arrReturn); return $objTemplate->parse(); } diff --git a/src/CoreBundle/EventListener/DcGeneral/MetaModel/CutButtonListener.php b/src/CoreBundle/EventListener/DcGeneral/MetaModel/CutButtonListener.php index 75ce260e9..047720b51 100644 --- a/src/CoreBundle/EventListener/DcGeneral/MetaModel/CutButtonListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/MetaModel/CutButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author David Molineus * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ViewHelpers; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use MetaModels\DcGeneral\DataDefinition\IMetaModelDataDefinition; /** @@ -44,11 +46,12 @@ public function handle(GetOperationButtonEvent $event) } $command = $event->getCommand(); + assert($command instanceof CommandInterface); if ($command->getName() === 'cut') { $sortingProperty = ViewHelpers::getManualSortingProperty($event->getEnvironment()); - if (!$sortingProperty) { + if (null === $sortingProperty) { $event->setDisabled(true); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/MetaModel/PasteButtonListener.php b/src/CoreBundle/EventListener/DcGeneral/MetaModel/PasteButtonListener.php index c28ad48bd..a936dad51 100644 --- a/src/CoreBundle/EventListener/DcGeneral/MetaModel/PasteButtonListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/MetaModel/PasteButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Stefan Heimes * @author David Molineus * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,16 +25,18 @@ use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; -use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteButtonEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteRootButtonEvent; use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use MetaModels\IFactory; /** * This class handles the paste into and after button activation and deactivation for all MetaModels being edited. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class PasteButtonListener { @@ -42,35 +45,35 @@ class PasteButtonListener * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The current environment. * - * @var EnvironmentInterface + * @var EnvironmentInterface|null */ - private $environment; + private EnvironmentInterface|null $environment = null; /** * The current data provider. * - * @var DataProviderInterface + * @var DataProviderInterface|null */ - private $provider; + private DataProviderInterface|null $provider = null; /** * The name of current data provider. * - * @var string + * @var string|null */ - private $providerName; + private string|null $providerName = null; /** * The model where we have to check if is it a paste into or paste after. * - * @var ModelInterface + * @var ModelInterface|null */ - private $currentModel; + private ModelInterface|null $currentModel = null; /** * Get determinator if there exists a circular reference. @@ -78,23 +81,23 @@ class PasteButtonListener * This flag determines if there exists a circular reference between the item currently in the clipboard and the * current model. A circular reference is of relevance when performing a cut and paste operation for example. * - * @var boolean + * @var boolean|null */ - private $circularReference; + private bool|null $circularReference = null; /** * Disable the paste after. * * @var bool */ - private $disablePA = true; + private bool $disablePA = true; /** * Disable the paste into. * * @var bool */ - private $disablePI = true; + private bool $disablePI = true; /** * Create a new instance. @@ -119,15 +122,21 @@ public function handle(GetPasteButtonEvent $event) { $this->circularReference = $event->isCircularReference(); $this->environment = $event->getEnvironment(); - $this->provider = $this->environment->getDataProvider(); - $this->providerName = $this->provider->getEmptyModel()->getProviderName(); - $clipboard = $this->environment->getClipboard(); - $this->currentModel = $event->getModel(); - $this->disablePI = true; - $this->disablePA = true; + $provider = $this->environment->getDataProvider(); + assert($provider instanceof DataProviderInterface); + $this->provider = $provider; + $this->providerName = $this->provider->getEmptyModel()->getProviderName(); + $clipboard = $this->environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); + $currentModel = $event->getModel(); + assert($currentModel instanceof ModelInterface); + $this->currentModel = $currentModel; + $this->disablePI = true; + $this->disablePA = true; // Only run for a MetaModels and if both values already disabled return here. - if ((substr($this->providerName, 0, 3) !== 'mm_') + if ( + (!\str_starts_with($this->providerName, 'mm_')) || ($event->isPasteIntoDisabled() && $event->isPasteAfterDisabled()) ) { return; @@ -153,15 +162,18 @@ public function handle(GetPasteButtonEvent $event) */ public function handleRoot(GetPasteRootButtonEvent $event) { - $this->environment = $event->getEnvironment(); - $this->provider = $this->environment->getDataProvider(); + $this->environment = $event->getEnvironment(); + $provider = $this->environment->getDataProvider(); + assert($provider instanceof DataProviderInterface); + $this->provider = $provider; $this->providerName = $this->provider->getEmptyModel()->getProviderName(); $clipboard = $this->environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $this->currentModel = null; $this->disablePI = false; // Only run for a MetaModels. - if ((substr($this->providerName, 0, 3) !== 'mm_') || $event->isPasteDisabled()) { + if ((!\str_starts_with($this->providerName, 'mm_')) || $event->isPasteDisabled()) { return; } @@ -173,24 +185,26 @@ public function handleRoot(GetPasteRootButtonEvent $event) } /** - * Find a item by its id. + * Find an item by its id. * - * @param int $modelId The id to find. + * @param mixed $modelId The id to find. * - * @return ModelInterface + * @return ModelInterface|null */ - private function getModelById($modelId) + private function getModelById(mixed $modelId): ?ModelInterface { if ($modelId === null) { return null; } - $provider = $this->environment->getDataProvider(); - $config = $provider - ->getEmptyConfig() - ->setId($modelId); + $environment = $this->environment; + assert($environment instanceof EnvironmentInterface); + $dataProvider = $environment->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); - return $provider->fetch($config); + $config = $dataProvider->getEmptyConfig()->setId($modelId); + + return $dataProvider->fetch($config); } /** @@ -200,12 +214,15 @@ private function getModelById($modelId) * * @throws \RuntimeException When the MetaModel can not be loaded. */ - private function hasVariants() + private function hasVariants(): bool { + if ($this->providerName === null) { + throw new \RuntimeException('No MetaModel name given'); + } $metaModel = $this->factory->getMetaModel($this->providerName); if ($metaModel === null) { - throw new \RuntimeException(sprintf('Could not find a MetaModels with the name %s', $this->providerName)); + throw new \RuntimeException(\sprintf('Could not find a MetaModel with the name %s', $this->providerName)); } return $metaModel->hasVariants(); @@ -215,24 +232,17 @@ private function hasVariants() * Check the buttons based on the action. * * @param ClipboardInterface $clipboard The clipboard. - * * @param string $action The action to be checked. * * @return void */ - private function checkForAction($clipboard, $action) + private function checkForAction(ClipboardInterface $clipboard, string $action): void { // Make a filter for the given action. $filter = new Filter(); $filter->andActionIs($action); $items = $clipboard->fetch($filter); - // Check if there are items. - if ($items === null) { - return; - } - - /** @var ItemInterface[] $items */ foreach ($items as $item) { // Check the context. $itemProviderName = $item->getDataProviderName(); @@ -248,7 +258,7 @@ private function checkForAction($clipboard, $action) } $containedModel = $this->getModelById($modelId->getId()); - if ($this->currentModel == null) { + if ($this->currentModel === null) { $this->checkForRoot($containedModel, $action); } elseif ($containedModel) { $this->checkForModel($containedModel, $action); @@ -265,11 +275,11 @@ private function checkForAction($clipboard, $action) * * @return void */ - private function checkEmpty($action) + private function checkEmpty(string $action): void { if ($this->hasVariants() && $this->currentModel !== null) { $this->disablePA = false; - } elseif ($action == 'create') { + } elseif ($action === 'create') { $this->disablePA = false; $this->disablePI = false; } @@ -278,15 +288,19 @@ private function checkEmpty($action) /** * Check the PI for the root element. * - * @param ModelInterface $containedModel The model with all data. - * - * @param string $action The action to be checked. + * @param ModelInterface|null $containedModel The model with all data. + * @param string $action The action to be checked. * * @return void */ - private function checkForRoot($containedModel, $action) + private function checkForRoot(?ModelInterface $containedModel, string $action): void { - if ($this->hasVariants() && $action == 'cut' && $containedModel->getProperty('varbase') == 0) { + if ( + $action === 'cut' + && null !== $containedModel + && $this->hasVariants() + && $containedModel->getProperty('varbase') === '' + ) { $this->disablePI = true; } } @@ -295,29 +309,34 @@ private function checkForRoot($containedModel, $action) * Check the PA and PI with a model. * * @param ModelInterface $containedModel The model with all data. - * * @param string $action The action to be checked. * * @return void */ - private function checkForModel($containedModel, $action) + private function checkForModel(ModelInterface $containedModel, string $action): void { if (!$this->circularReference) { if ($this->hasVariants()) { $this->checkModelWithVariants($containedModel); } $this->checkModelWithoutVariants($containedModel); - } elseif ($this->currentModel == null && $containedModel->getProperty('varbase') == 0) { - $this->disablePA = true; - } else { - $this->disablePA = false; - // The following rules apply: - // 1. Variant bases must not get pasted into anything. - // 2. If we are not in create mode, disable the paste into for the item itself. - $this->disablePI = - ($this->hasVariants() && $containedModel->getProperty('varbase') == 1) - || ($action != 'create' && $containedModel->getId() == $this->currentModel->getId()); + return; } + + if ($this->currentModel === null) { + if ($containedModel->getProperty('varbase') === '') { + $this->disablePA = true; + } + + return; + } + $this->disablePA = false; + // The following rules apply: + // 1. Variant bases must not get pasted into anything. + // 2. If we are not in create mode, disable the paste into for the item itself. + $this->disablePI = + ($this->hasVariants() && $containedModel->getProperty('varbase') === '1') + || ($action !== 'create' && $containedModel->getId() === $this->currentModel->getId()); } /** @@ -327,19 +346,19 @@ private function checkForModel($containedModel, $action) * * @return void */ - private function checkModelWithVariants($containedModel) + private function checkModelWithVariants(ModelInterface $containedModel): void { // Item and variant support. $isVarbase = (bool) $containedModel->getProperty('varbase'); $vargroup = $containedModel->getProperty('vargroup'); - $isCurrentVarbase = (bool) $this->currentModel->getProperty('varbase'); - $currentVargroup = $this->currentModel->getProperty('vargroup'); + $isCurrentVarbase = (bool) $this->currentModel?->getProperty('varbase'); + $currentVargroup = $this->currentModel?->getProperty('vargroup'); if ($isVarbase && !$this->circularReference && $isCurrentVarbase) { // Insert new items only after bases. // Insert a varbase after any other varbase, for sorting. $this->disablePA = false; - } elseif (!$isVarbase && !$isCurrentVarbase && $vargroup == $currentVargroup) { + } elseif (!$isVarbase && !$isCurrentVarbase && $vargroup === $currentVargroup) { // Move items in their vargroup and only there. $this->disablePA = false; } @@ -354,14 +373,20 @@ private function checkModelWithVariants($containedModel) * * @return void */ - private function checkModelWithoutVariants($containedModel) + private function checkModelWithoutVariants(ModelInterface $containedModel): void { - $parentDefinition = $this->environment->getDataDefinition()->getBasicDefinition()->getParentDataProvider(); - - $this->disablePA = ($this->currentModel->getId() == $containedModel->getId()) - || ($parentDefinition && $this->currentModel->getProperty('pid') == $containedModel->getProperty('pid')); - $this->disablePI = ($this->circularReference) - || ($this->currentModel->getId() == $containedModel->getId()) - || ($parentDefinition && $this->currentModel->getProperty('pid') == $containedModel->getId()); + $environment = $this->environment; + assert($environment instanceof EnvironmentInterface); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $parentDefinition = null !== $dataDefinition->getBasicDefinition()->getParentDataProvider(); + $currentModelId = $this->currentModel?->getId(); + $currentModelPid = $this->currentModel?->getProperty('pid'); + $this->disablePA = ($currentModelId === $containedModel->getId()) + || ($parentDefinition && $currentModelPid === $containedModel->getProperty('pid')); + $this->disablePI = ($this->circularReference) + || ($currentModelId === $containedModel->getId()) + || ($parentDefinition && $currentModelPid === $containedModel->getId()); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/AbstractPaletteRestrictionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/AbstractPaletteRestrictionListener.php index c424f18e0..7207eb2f3 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/AbstractPaletteRestrictionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/AbstractPaletteRestrictionListener.php @@ -34,7 +34,6 @@ */ class AbstractPaletteRestrictionListener { - /** * Retrieve the legend with the given name. * @@ -94,7 +93,8 @@ protected function getProperty($name, $legend) protected function addCondition($property, $condition) { $currentCondition = $property->getVisibleCondition(); - if ((!($currentCondition instanceof ConditionChainInterface)) + if ( + (!($currentCondition instanceof ConditionChainInterface)) || (ConditionChainInterface::OR_CONJUNCTION !== $currentCondition->getConjunction()) ) { if ($currentCondition === null) { diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeCreateListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeCreateListener.php index 59096a4ea..d19a262e4 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeCreateListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeCreateListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package MetaModels/core * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +22,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Event\PreEditModelEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use Doctrine\DBAL\Connection; use MetaModels\Attribute\IAttributeFactory; use MetaModels\IFactory; @@ -69,9 +70,13 @@ public function handle(PreEditModelEvent $event): void return; } - if ('create' !== $event->getEnvironment()->getInputProvider()->getParameter('act') - || 'snc' !== $event->getEnvironment()->getInputProvider()->getParameter('btn') - || !($after = $event->getEnvironment()->getInputProvider()->getParameter('after')) + $inputProvider = $event->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ( + 'create' !== $inputProvider->getParameter('act') + || 'snc' !== $inputProvider->getParameter('btn') + || !($after = $inputProvider->getParameter('after')) ) { return; } @@ -87,7 +92,7 @@ public function handle(PreEditModelEvent $event): void ->where('t.id=:id') ->setParameter('id', $previousAttributeId) ->setMaxResults(1) - ->execute() + ->executeQuery() ->fetchFirstColumn(); if (empty($statement)) { diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeDeletedListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeDeletedListener.php deleted file mode 100644 index bdc0dc70b..000000000 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeDeletedListener.php +++ /dev/null @@ -1,98 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute; - -use ContaoCommunityAlliance\DcGeneral\Event\PreDeleteModelEvent; - -/** - * This class takes care of deleting an attribute. - */ -class AttributeDeletedListener extends BaseListener -{ - /** - * Handle the update of an attribute and all attached data. - * - * @param PreDeleteModelEvent $event The event. - * - * @return void - */ - public function handle(PreDeleteModelEvent $event) - { - if (!$this->wantToHandle($event)) { - return; - } - - if ($attribute = $this->createAttributeInstance($event->getModel())) { - $this->deleteConditionSettings($event); - $attribute->destroyAUX(); - } - } - - /** - * Delete joint condition setting with attribute. - * - * @param PreDeleteModelEvent $event The event. - * - * @return void - */ - protected function deleteConditionSettings(PreDeleteModelEvent $event) - { - $environment = $event->getEnvironment(); - $model = $event->getModel(); - $dataProvider = $environment->getDataProvider('tl_metamodel_dcasetting_condition'); - - $conditions = $dataProvider->fetchAll( - $dataProvider->getEmptyConfig()->setFilter( - [['operation' => '=', 'property' => 'attr_id', 'value' => $model->getId()]] - ) - ); - - if ($conditions->count() < 1) { - return; - } - - $conditionsGeneral = new \DC_General($dataProvider->getEmptyModel()->getProviderName()); - $conditionsEnvironment = $conditionsGeneral->getEnvironment(); - $conditionsDataDefinition = $conditionsEnvironment->getDataDefinition(); - $conditionsPalettesDefinition = $conditionsDataDefinition->getPalettesDefinition(); - - /** @var \Iterator $conditionsIterator */ - $conditionsIterator = $conditions->getIterator(); - while ($currentCondition = $conditionsIterator->current()) { - $conditionPalette = $conditionsPalettesDefinition->getPaletteByName( - $currentCondition->getProperty('type') - ); - $conditionProperties = $conditionPalette->getVisibleProperties( - $currentCondition - ); - - foreach ($conditionProperties as $conditionProperty) { - if ($conditionProperty->getName() !== 'attr_id') { - continue; - } - - $dataProvider->delete($currentCondition); - } - - $conditionsIterator->next(); - } - } -} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeRendererListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeRendererListener.php index 85bf19b24..5e57b2e0f 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeRendererListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeRendererListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +22,10 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute; use Contao\StringUtil; +use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use MetaModels\ITranslatedMetaModel; /** * This renders attribute information in the backend listing. @@ -49,10 +52,10 @@ public function modelToLabel(ModelToLabelEvent $event) $attribute = $this->attributeFactory->createAttribute($model->getPropertiesAsArray(), $metaModel); if (!$attribute) { - $translator = $event - ->getEnvironment() - ->getTranslator(); + $translator = $event->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + /** @psalm-suppress InvalidArgument */ $event ->setLabel( '
%s [%s]
@@ -61,10 +64,11 @@ public function modelToLabel(ModelToLabelEvent $event) ' ) ->setArgs([ - $translator->translate('error_unknown_attribute.0', 'tl_metamodel_attribute'), + $translator->translate('error_unknown_attribute.label', 'tl_metamodel_attribute'), $type, - $translator->translate('error_unknown_attribute.1', 'tl_metamodel_attribute', [$type]), + $translator->translate('error_unknown_attribute.description', 'tl_metamodel_attribute', [$type]), ]); + return; } @@ -72,15 +76,24 @@ public function modelToLabel(ModelToLabelEvent $event) $colName = $attribute->getColName(); $name = $attribute->getName(); $arrDescription = StringUtil::deserialize($attribute->get('description')); - if (is_array($arrDescription)) { - $description = $arrDescription[$attribute->getMetaModel()->getActiveLanguage()]; - if (!$description) { - $description = $arrDescription[$attribute->getMetaModel()->getFallbackLanguage()]; + if (\is_array($arrDescription)) { + $locale = (string) System::getContainer()->get('request_stack')?->getCurrentRequest()?->getLocale(); + $description = $arrDescription[$locale] ?? null; + /** @psalm-suppress DeprecatedMethod */ + if (null === $description) { + if ($metaModel instanceof ITranslatedMetaModel) { + $description = $arrDescription[$metaModel->getMainLanguage()] ?? $attribute->getName(); + } else { + /** @psalm-suppress DeprecatedMethod */ + $description = $arrDescription[(string) $attribute->getMetaModel()->getFallbackLanguage()] + ?? $attribute->getName(); + } } } else { $description = $arrDescription ?: $attribute->getName(); } + /** @psalm-suppress InvalidArgument */ $event ->setLabel( '
%s [%s%s]
diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeSavedListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeSavedListener.php deleted file mode 100644 index 8f749c329..000000000 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeSavedListener.php +++ /dev/null @@ -1,83 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute; - -use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; - -/** - * This class takes care of updating all data when an attribute has been saved. - */ -class AttributeSavedListener extends BaseListener -{ - /** - * Handle the update of an attribute and all attached data. - * - * @param PostPersistModelEvent $event The event. - * - * @return void - */ - public function handle(PostPersistModelEvent $event) - { - if (!$this->wantToHandle($event)) { - return; - } - - $old = $event->getOriginalModel(); - $new = $event->getModel(); - $oldInstance = $old->getProperty('pid') ? $this->createAttributeInstance($old) : null; - $newInstance = $this->createAttributeInstance($new); - - // If type or column name has been changed, destroy old data and initialize new. - if ($this->isAttributeNameOrTypeChanged($new, $old)) { - // Destroy old instance. - if ($oldInstance) { - $oldInstance->destroyAUX(); - } - - // Create new instance aux info. - if ($newInstance) { - $newInstance->initializeAUX(); - } - } - - if ($newInstance) { - // Now loop over all values and update the meta in the instance. - foreach ($new->getPropertiesAsArray() as $strKey => $varValue) { - $newInstance->handleMetaChange($strKey, $varValue); - } - } - } - - /** - * Check if either type or column name have been changed within the model. - * - * @param ModelInterface $new The new model. - * @param ModelInterface $old The old model (or null). - * - * @return bool - */ - private function isAttributeNameOrTypeChanged(ModelInterface $new, ModelInterface $old) - { - return ($old->getProperty('type') !== $new->getProperty('type')) - || ($old->getProperty('colname') !== $new->getProperty('colname')); - } -} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeSchemaManagerHintListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeSchemaManagerHintListener.php new file mode 100644 index 000000000..ff56056dd --- /dev/null +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/AttributeSchemaManagerHintListener.php @@ -0,0 +1,75 @@ + + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute; + +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Event\PreEditModelEvent; +use Contao\Message; +use MetaModels\Attribute\IAttributeFactory; +use MetaModels\IFactory; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * This class takes care of updating all data when an attribute has been saved. + */ +class AttributeSchemaManagerHintListener extends BaseListener +{ + /** + * The translator. + * + * @var TranslatorInterface + */ + private $translator; + + + /** + * Create a new instance. + * + * @param RequestScopeDeterminator $scopeDeterminator The scope determinator. + * @param IAttributeFactory $attributeFactory The attribute factory. + * @param IFactory $factory The MetaModel factory. + * @param TranslatorInterface $translator The translator. + */ + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + IAttributeFactory $attributeFactory, + IFactory $factory, + TranslatorInterface $translator + ) { + parent::__construct($scopeDeterminator, $attributeFactory, $factory); + $this->translator = $translator; + } + + /** + * Add hint at attribute. + * + * @param PreEditModelEvent $event The event. + * + * @return void + */ + public function handle(PreEditModelEvent $event): void + { + if (!$this->wantToHandle($event)) { + return; + } + + Message::addInfo($this->translator->trans('hint_schema_manager', [], 'tl_metamodel_attribute')); + } +} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/BaseListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/BaseListener.php index 12c1b81b4..85a43ee20 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/BaseListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/BaseListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -22,6 +23,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; use MetaModels\Attribute\IAttribute; @@ -39,7 +41,7 @@ class BaseListener * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * The attribute factory. @@ -130,13 +132,15 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) return false; } - $environment = $event->getEnvironment(); - if ('tl_metamodel_attribute' !== $environment->getDataDefinition()->getName()) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ('tl_metamodel_attribute' !== $dataDefinition->getName()) { return false; } if ($event instanceof AbstractModelAwareEvent) { - if ($event->getEnvironment()->getDataDefinition()->getName() !== $event->getModel()->getProviderName()) { + if ($dataDefinition->getName() !== $event->getModel()->getProviderName()) { return false; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/ColNameValidationListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/ColNameValidationListener.php index 0d97c32ae..6d673daf2 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/ColNameValidationListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/ColNameValidationListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,9 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use MetaModels\Attribute\IAttributeFactory; +use MetaModels\Exceptions\Database\InvalidColumnNameException; use MetaModels\Helper\TableManipulator; use MetaModels\IFactory; @@ -36,7 +39,7 @@ class ColNameValidationListener extends BaseListener * * @var TableManipulator */ - private $tableManipulator; + private TableManipulator $tableManipulator; /** * Create a new instance. @@ -65,7 +68,7 @@ public function __construct( * * @throws \RuntimeException When the column name is illegal or duplicate. */ - public function handle(EncodePropertyValueFromWidgetEvent $event) + public function handle(EncodePropertyValueFromWidgetEvent $event): void { if (!parent::wantToHandle($event) || ($event->getProperty() !== 'colname')) { return; @@ -74,17 +77,39 @@ public function handle(EncodePropertyValueFromWidgetEvent $event) $oldColumnName = $event->getModel()->getProperty($event->getProperty()); $columnName = $event->getValue(); $metaModel = $this->getMetaModelByModelPid($event->getModel()); + $translator = $event->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); if ((!$columnName) || $oldColumnName !== $columnName) { - $this->tableManipulator->checkColumnDoesNotExist($metaModel->getTableName(), $columnName); + try { + $this->tableManipulator->checkColumnName($columnName); + } catch (InvalidColumnNameException $exception) { + throw new \RuntimeException( + $translator->translate( + 'ERR.' . ($this->tableManipulator->isSystemColumn($columnName) + ? 'systemColumn' + : 'invalidColumnName'), + 'tl_metamodel_attribute', + [ + '%col_name%' => $columnName, + '%table_name%' => $metaModel->getTableName() + ] + ), + 0, + $exception + ); + } - $colNames = array_keys($metaModel->getAttributes()); - if (in_array($columnName, $colNames)) { + $colNames = \array_keys($metaModel->getAttributes()); + if (\in_array($columnName, $colNames)) { throw new \RuntimeException( - sprintf( - $event->getEnvironment()->getTranslator()->translate('columnExists', 'ERR'), - $columnName, - $metaModel->getTableName() + $translator->translate( + 'ERR.columnExists', + 'tl_metamodel_attribute', + [ + '%col_name%' => $columnName, + '%table_name%' => $metaModel->getTableName() + ] ) ); } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/GetAttributeTypeListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/GetAttributeTypeListener.php index 85e37cfff..da072cb3b 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/GetAttributeTypeListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/GetAttributeTypeListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,8 +24,11 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use MetaModels\Attribute\IAttributeFactory; +use MetaModels\Attribute\IAttributeTypeFactory; use MetaModels\IFactory; +use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; /** @@ -70,10 +74,13 @@ public function getOptions(GetPropertyOptionsEvent $event) return; } - $translator = $event->getEnvironment()->getTranslator(); + $translator = $event->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); + $objMetaModel = $this->getMetaModelByModelPid($event->getModel()); $flags = IAttributeFactory::FLAG_ALL_UNTRANSLATED; + /** @psalm-suppress DeprecatedMethod */ if ($objMetaModel->isTranslated()) { $flags |= IAttributeFactory::FLAG_INCLUDE_TRANSLATED; } @@ -82,7 +89,9 @@ public function getOptions(GetPropertyOptionsEvent $event) $optionsTrans = []; foreach ($this->attributeFactory->getTypeNames($flags) as $attributeType) { // Differentiate translated and simple. - if ($this->attributeFactory->getTypeFactory($attributeType)->isTranslatedType()) { + $typeFactory = $this->attributeFactory->getTypeFactory($attributeType); + assert($typeFactory instanceof IAttributeTypeFactory); + if ($typeFactory->isTranslatedType()) { $optionsTrans[$attributeType] = $translator->translate( 'typeOptions.' . $attributeType, 'tl_metamodel_attribute' @@ -94,12 +103,13 @@ public function getOptions(GetPropertyOptionsEvent $event) ); } } - asort($options); + \asort($options); // Add translated attributes. + /** @psalm-suppress DeprecatedMethod */ if ($objMetaModel->isTranslated()) { - asort($optionsTrans); - $options = array_merge($options, $optionsTrans); + \asort($optionsTrans); + $options = \array_merge($options, $optionsTrans); } $event->setOptions($options); @@ -123,6 +133,8 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) } $request = $this->requestStack->getCurrentRequest(); + assert($request instanceof Request); + if ($request->request->get('act', null) === 'select' && !$event->getModel()->getId()) { return false; } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/GetVariantListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/GetVariantListener.php new file mode 100644 index 000000000..e23581afd --- /dev/null +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/GetVariantListener.php @@ -0,0 +1,70 @@ + + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute; + +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; +use MetaModels\Attribute\IAttributeFactory; +use MetaModels\IFactory; + +/** + * This class provides the attribute variant activation. + */ +class GetVariantListener extends BaseListener +{ + /** + * Create a new instance. + * + * @param RequestScopeDeterminator $scopeDeterminator The scope determinator. + * @param IAttributeFactory $attributeFactory The attribute factory. + * @param IFactory $factory The MetaModel factory. + */ + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + IAttributeFactory $attributeFactory, + IFactory $factory + ) { + parent::__construct($scopeDeterminator, $attributeFactory, $factory); + } + + /** + * Set widget disabled/readonly if model not variant. + * + * @param BuildWidgetEvent $event The event. + * + * @return void + */ + public function buildWidget(BuildWidgetEvent $event) + { + if (!($this->wantToHandle($event) && 'isvariant' === $event->getProperty()->getName())) { + return; + } + + $model = $event->getModel(); + $metaModel = $this->getMetaModelByModelPid($model); + + if (!$metaModel->hasVariants()) { + $extra = $event->getProperty()->getExtra(); + $extra['disabled'] = true; + $event->getProperty()->setExtra($extra); + $model->setProperty('readonly', true); + } + } +} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/NameAndDescriptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/NameAndDescriptionListener.php index ef06f56a0..caadfddda 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/NameAndDescriptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Attribute/NameAndDescriptionListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Stefan Heimes - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -29,7 +30,10 @@ use MetaModels\Attribute\IAttributeFactory; use MetaModels\Dca\Helper; use MetaModels\IFactory; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function in_array; +use function unserialize; /** * This class provides the attribute type names. @@ -41,7 +45,7 @@ class NameAndDescriptionListener extends BaseListener * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * Create a new instance. @@ -70,7 +74,7 @@ public function __construct( */ public function decodeValue(DecodePropertyValueForWidgetEvent $event) { - if (!($this->wantToHandle($event) && \in_array($event->getProperty(), ['name', 'description']))) { + if (!($this->wantToHandle($event) && in_array($event->getProperty(), ['name', 'description']))) { return; } @@ -89,7 +93,7 @@ public function decodeValue(DecodePropertyValueForWidgetEvent $event) */ public function encodeValue(EncodePropertyValueFromWidgetEvent $event) { - if (!($this->wantToHandle($event) && \in_array($event->getProperty(), ['name', 'description']))) { + if (!($this->wantToHandle($event) && in_array($event->getProperty(), ['name', 'description']))) { return; } $metaModel = $this->getMetaModelByModelPid($event->getModel()); @@ -107,7 +111,7 @@ public function encodeValue(EncodePropertyValueFromWidgetEvent $event) */ public function buildWidget(BuildWidgetEvent $event) { - if (!($this->wantToHandle($event) && \in_array($event->getProperty()->getName(), ['name', 'description']))) { + if (!($this->wantToHandle($event) && in_array($event->getProperty()->getName(), ['name', 'description']))) { return; } @@ -117,8 +121,8 @@ public function buildWidget(BuildWidgetEvent $event) $event->getEnvironment(), $event->getProperty(), $metaModel, - $this->translator->trans('tl_metamodel_attribute.name_langcode', [], 'contao_tl_metamodel_attribute'), - $this->translator->trans('tl_metamodel_attribute.name_value', [], 'contao_tl_metamodel_attribute'), + 'name_langcode', + 'name_value', false, StringUtil::deserialize($event->getModel()->getProperty($event->getProperty()->getName()), true) ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/BackendSectionOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/BackendSectionOptionListener.php index 86661c9ef..b3fc1af91 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/BackendSectionOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/BackendSectionOptionListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,20 +13,37 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Dca; +use Contao\Controller; +use Contao\CoreBundle\Framework\ContaoFramework; +use Contao\CoreBundle\Menu\BackendMenuBuilder; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use Knp\Menu\ItemInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function array_diff; +use function array_keys; /** * This provides the backend section options. */ class BackendSectionOptionListener { + public function __construct( + private BackendMenuBuilder $builder, + private TranslatorInterface $translator, + private ContaoFramework $framework, + ) { + } + /** * Retrieve a list of all backend sections, like "content", "system" etc. * @@ -39,11 +56,36 @@ class BackendSectionOptionListener */ public function handle(GetPropertyOptionsEvent $event) { - if (('tl_metamodel_dca' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('backendsection' !== $event->getPropertyName())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_dca' !== $dataDefinition->getName()) + || ('backendsection' !== $event->getPropertyName()) + ) { return; } - $event->setOptions(array_keys($GLOBALS['BE_MOD'])); + $options = []; + foreach ($this->getMenu()->getChildren() as $option) { + $label = $option->getLabel(); + if (false !== $domain = $option->getExtra('translation_domain')) { + $label = $this->translator->trans($label, $option->getExtra('translation_params') ?? [], $domain); + } + + $options[$option->getName()] = $label; + } + + $event->setOptions($options); + } + + private function getMenu(): ItemInterface + { + // Work around legacy Contao code - Menu builder loads via global lang array instead of translator. + /** @psalm-suppress InternalMethod - Class ContaoFramework is internal, not the getAdapter() method. */ + $contaoController = $this->framework->getAdapter(Controller::class); + $contaoController->loadLanguageFile('modules'); + + return $this->builder->buildMainMenu(); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/ParentTableOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/ParentTableOptionListener.php index e99c47664..d3fdf6fc7 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/ParentTableOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/ParentTableOptionListener.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. @@ -13,7 +13,7 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,6 +21,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Dca; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use Doctrine\DBAL\Connection; use MetaModels\IFactory; @@ -34,14 +35,14 @@ class ParentTableOptionListener * * @var Connection */ - private $connection; + private Connection $connection; /** * The MetaModel factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Create a new instance. @@ -67,21 +68,26 @@ public function __construct(Connection $connection, IFactory $factory) */ public function handle(GetPropertyOptionsEvent $event) { - if (('tl_metamodel_dca' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('ptable' !== $event->getPropertyName())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_dca' !== $dataDefinition->getName()) + || ('ptable' !== $event->getPropertyName()) + ) { return; } $tables = []; - foreach ($this->connection->getSchemaManager()->listTableNames() as $table) { + foreach ($this->connection->createSchemaManager()->listTableNames() as $table) { $tables[$table] = $table; } if ('ctable' === $event->getModel()->getProperty('rendertype')) { $currentTable = $this->factory->translateIdToMetaModelName($event->getModel()->getProperty('pid')); - $tables = array_filter( + $tables = \array_filter( $tables, - function ($table) use ($currentTable) { + static function ($table) use ($currentTable) { return ($currentTable !== $table); } ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/ParentTableVisibilityListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/ParentTableVisibilityListener.php index 8de2e2b26..8727c6c65 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/ParentTableVisibilityListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/ParentTableVisibilityListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -40,17 +41,19 @@ public function handle(BuildDataDefinitionEvent $event) { foreach ($event->getContainer()->getPalettesDefinition()->getPalettes() as $palette) { foreach ($palette->getProperties() as $property) { - if ($property->getName() != 'ptable') { + if ($property->getName() !== 'ptable') { continue; } $chain = $property->getVisibleCondition(); - if (!($chain + if ( + !($chain && ($chain instanceof PropertyConditionChain) && $chain->getConjunction() == PropertyConditionChain::AND_CONJUNCTION - )) { + ) + ) { $chain = new PropertyConditionChain( - array($property->getVisibleCondition()), + [$property->getVisibleCondition()], PropertyConditionChain::AND_CONJUNCTION ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderModeHintListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderModeHintListener.php new file mode 100644 index 000000000..74358c1b7 --- /dev/null +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderModeHintListener.php @@ -0,0 +1,82 @@ + + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Dca; + +use Contao\Message; +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use Doctrine\DBAL\Connection; +use MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting\AbstractListener; +use MetaModels\IFactory; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * This adds a hint if render mode hierarchical. + */ +class RenderModeHintListener +{ + /** + * The translator. + * + * @var TranslatorInterface + */ + private TranslatorInterface $translator; + + /** + * Create a new instance. + * + * @param TranslatorInterface $translator The translator. + */ + public function __construct( + TranslatorInterface $translator + ) { + $this->translator = $translator; + } + + /** + * Adds a hint if render mode hierarchical. + * + * @param BuildWidgetEvent $event The event. + * + * @return void + */ + public function handle(BuildWidgetEvent $event) + { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ($dataDefinition->getName() !== 'tl_metamodel_dca') + || ($event->getProperty()->getName() !== 'rendermode') + || (null === $event->getModel()->getId()) + ) { + return; + } + + $model = $event->getModel(); + + if ('hierarchical' === $model->getProperty('rendermode')) { + Message::addInfo( + $this->translator->trans('hint_rendermode_hierarchical', [], 'tl_metamodel_dca') + ); + } + } +} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderModeOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderModeOptionListener.php index c30c138f6..40ff3b6bf 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderModeOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderModeOptionListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -21,6 +22,8 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Dca; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; /** * This provides the render mode options. @@ -39,13 +42,20 @@ class RenderModeOptionListener */ public function handle(GetPropertyOptionsEvent $event) { - if (('tl_metamodel_dca' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('rendermode' !== $event->getPropertyName())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_dca' !== $dataDefinition->getName()) + || ('rendermode' !== $event->getPropertyName()) + ) { return; } $translator = $event->getEnvironment()->getTranslator(); - $options = [ + assert($translator instanceof TranslatorInterface); + + $options = [ 'flat' => $translator->translate('rendermodes.flat', 'tl_metamodel_dca'), 'hierarchical' => $translator->translate('rendermodes.hierarchical', 'tl_metamodel_dca'), ]; diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderTypeOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderTypeOptionListener.php index 1c9dde9e3..c03b46b78 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderTypeOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/Dca/RenderTypeOptionListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -21,6 +22,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\Dca; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; /** * This provides the render type options. @@ -39,8 +41,13 @@ class RenderTypeOptionListener */ public function handle(GetPropertyOptionsEvent $event) { - if (('tl_metamodel_dca' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('rendertype' !== $event->getPropertyName())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_dca' !== $dataDefinition->getName()) + || ('rendertype' !== $event->getPropertyName()) + ) { return; } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/FixSortingListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/FixSortingListener.php index 57e0c9e5d..e59c7f09f 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/FixSortingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/FixSortingListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -21,6 +22,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaCombine; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; /** * This class optimizes the sorting values. @@ -36,15 +38,20 @@ class FixSortingListener */ public function handle(EncodePropertyValueFromWidgetEvent $event) { - if (('tl_metamodel_dca_combine' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('rows' !== $event->getProperty())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_dca_combine' !== $dataDefinition->getName()) + || ('rows' !== $event->getProperty()) + ) { return; } $values = $event->getValue(); $index = 0; - $time = time(); - foreach (array_keys($values) as $key) { + $time = \time(); + foreach (\array_keys($values) as $key) { $values[$key]['sorting'] = $index; $values[$key]['tstamp'] = $time; diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/FixTypeSafetyListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/FixTypeSafetyListener.php index 6b96f477b..4e79c0f96 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/FixTypeSafetyListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/FixTypeSafetyListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/core * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -20,8 +21,10 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaCombine; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; -use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DefaultDataProvider; use ContaoCommunityAlliance\DcGeneral\Data\ModelManipulator; +use ContaoCommunityAlliance\DcGeneral\Data\TableRowsAsRecordsDataProvider; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\PropertiesDefinitionInterface; /** @@ -38,14 +41,21 @@ class FixTypeSafetyListener */ public function handle(EncodePropertyValueFromWidgetEvent $event) { - if (('tl_metamodel_dca_combine' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('rows' !== $event->getProperty())) { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_dca_combine' !== $dataDefinition->getName()) + || ('rows' !== $event->getProperty()) + ) { return; } - $environment = $event->getEnvironment(); + $dataProvider = $environment->getDataProvider(); - $properties = $environment->getDataDefinition()->getPropertiesDefinition(); + assert($dataProvider instanceof TableRowsAsRecordsDataProvider); + $properties = $dataDefinition->getPropertiesDefinition(); $values = (array) $event->getValue(); foreach ($values as $row => $current) { @@ -60,17 +70,18 @@ public function handle(EncodePropertyValueFromWidgetEvent $event) * * @param array $values The values for update. * @param PropertiesDefinitionInterface $properties The properties. - * @param DataProviderInterface $dataProvider The data provider. + * @param TableRowsAsRecordsDataProvider $dataProvider The data provider. * * @return array */ private function updateValues( array &$values, PropertiesDefinitionInterface $properties, - DataProviderInterface $dataProvider + TableRowsAsRecordsDataProvider $dataProvider ) { foreach ($values as $propertyName => $propertyValue) { - if (($dataProvider->getIdProperty() === $propertyName) + if ( + ($dataProvider->getIdProperty() === $propertyName) || ($dataProvider->getGroupColumnProperty() === $propertyName) || ($dataProvider->getSortingColumnProperty() === $propertyName) || ($dataProvider->getTimeStampProperty() === $propertyName) diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/GroupOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/GroupOptionListener.php index 672ae1148..7d5d9120a 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/GroupOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/GroupOptionListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,15 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaCombine; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use Doctrine\DBAL\Connection; use MenAtWork\MultiColumnWizardBundle\Event\GetOptionsEvent; @@ -34,7 +36,7 @@ class GroupOptionListener * * @var Connection */ - private $connection; + private Connection $connection; /** * Create a new instance. @@ -55,9 +57,14 @@ public function __construct(Connection $connection) */ public function handle(GetOptionsEvent $event) { - if (('tl_metamodel_dca_combine' !== $event->getEnvironment()->getDataDefinition()->getName()) + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_dca_combine' !== $dataDefinition->getName()) || ('rows' !== $event->getPropertyName()) - || !in_array($event->getSubPropertyName(), ['be_group', 'fe_group'])) { + || !\in_array($event->getSubPropertyName(), ['be_group', 'fe_group']) + ) { return; } @@ -68,11 +75,14 @@ public function handle(GetOptionsEvent $event) ->select('t.id') ->addSelect('t.name') ->from($isBackend ? 'tl_user_group' : 'tl_member_group', 't') - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); + + $translator = $event->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); $result = []; - $result[-1] = $event->getEnvironment()->getTranslator()->translate( + $result[-1] = $translator->translate( $isBackend ? 'sysadmin' : 'anonymous', 'tl_metamodel_dca_combine' ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/InputScreenOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/InputScreenOptionListener.php index 98ced78a9..c633cdcae 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/InputScreenOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/InputScreenOptionListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,14 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaCombine; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use Doctrine\DBAL\Connection; use MenAtWork\MultiColumnWizardBundle\Event\GetOptionsEvent; @@ -34,7 +35,7 @@ class InputScreenOptionListener * * @var Connection */ - private $connection; + private Connection $connection; /** * Create a new instance. @@ -55,9 +56,14 @@ public function __construct(Connection $connection) */ public function handle(GetOptionsEvent $event) { - if (('tl_metamodel_dca_combine' !== $event->getEnvironment()->getDataDefinition()->getName()) + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_dca_combine' !== $dataDefinition->getName()) || ('rows' !== $event->getPropertyName()) - || ('dca_id' !== $event->getSubPropertyName())) { + || ('dca_id' !== $event->getSubPropertyName()) + ) { return; } @@ -69,8 +75,8 @@ public function handle(GetOptionsEvent $event) ->from('tl_metamodel_dca', 't') ->where('t.pid=:pid') ->setParameter('pid', $event->getModel()->getProperty('id')) - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); $result = []; foreach ($screens as $screen) { diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/RenderSettingOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/RenderSettingOptionListener.php index 8d16cde12..1f0fe2d9d 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/RenderSettingOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaCombine/RenderSettingOptionListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,14 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaCombine; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use Doctrine\DBAL\Connection; use MenAtWork\MultiColumnWizardBundle\Event\GetOptionsEvent; @@ -34,7 +35,7 @@ class RenderSettingOptionListener * * @var Connection */ - private $connection; + private Connection $connection; /** * Create a new instance. @@ -55,9 +56,14 @@ public function __construct(Connection $connection) */ public function handle(GetOptionsEvent $event) { - if (('tl_metamodel_dca_combine' !== $event->getEnvironment()->getDataDefinition()->getName()) + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_dca_combine' !== $dataDefinition->getName()) || ('rows' !== $event->getPropertyName()) - || 'view_id' !== $event->getSubPropertyName()) { + || 'view_id' !== $event->getSubPropertyName() + ) { return; } @@ -69,8 +75,8 @@ public function handle(GetOptionsEvent $event) ->from('tl_metamodel_rendersettings', 't') ->where('t.pid=:pid') ->setParameter('pid', $event->getModel()->getProperty('id')) - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); $result = []; foreach ($screens as $screen) { diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AbstractAbstainingListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AbstractAbstainingListener.php index 4d4f1d27a..59b478bef 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AbstractAbstainingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AbstractAbstainingListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -21,6 +22,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; @@ -34,7 +36,7 @@ abstract class AbstractAbstainingListener * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * Create a new instance. @@ -60,13 +62,15 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) return false; } - $environment = $event->getEnvironment(); - if ('tl_metamodel_dcasetting' !== $environment->getDataDefinition()->getName()) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ('tl_metamodel_dcasetting' !== $dataDefinition->getName()) { return false; } if ($event instanceof AbstractModelAwareEvent) { - if ($event->getEnvironment()->getDataDefinition()->getName() !== $event->getModel()->getProviderName()) { + if ($dataDefinition->getName() !== $event->getModel()->getProviderName()) { return false; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AbstractListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AbstractListener.php index 4d094f2b2..820970ec7 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AbstractListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AbstractListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -69,15 +69,16 @@ public function __construct( * * @param ModelInterface $model The input screen model for which to retrieve the MetaModel. * - * @return IMetaModel + * @return IMetaModel|null * * @throws DcGeneralInvalidArgumentException When an invalid model has been passed or the model does not have an id. + * @throws \Doctrine\DBAL\Exception */ protected function getMetaModelFromModel(ModelInterface $model) { - if (!(($model->getProviderName() == 'tl_metamodel_dcasetting') && $model->getProperty('pid'))) { + if (!(($model->getProviderName() === 'tl_metamodel_dcasetting') && $model->getProperty('pid'))) { throw new DcGeneralInvalidArgumentException( - sprintf( + \sprintf( 'Model must originate from tl_metamodel_dcasetting and be saved, this one originates from %s and ' . 'has pid %s', $model->getProviderName(), @@ -92,8 +93,12 @@ protected function getMetaModelFromModel(ModelInterface $model) ->from('tl_metamodel_dca', 't') ->where('t.id=:id') ->setParameter('id', $model->getProperty('pid')) - ->execute() - ->fetchColumn(); + ->executeQuery() + ->fetchOne(); + + if (false === $metaModelId) { + return null; + } $tableName = $this->factory->translateIdToMetaModelName($metaModelId); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AddAllButtonListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AddAllButtonListener.php index 6f2e2055f..ede039e7e 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AddAllButtonListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AddAllButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +23,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use Doctrine\DBAL\Connection; use MetaModels\IFactory; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -36,21 +39,21 @@ class AddAllButtonListener * * @var Connection */ - private $connection; + private Connection $connection; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The URL generator. * * @var UrlGeneratorInterface */ - private $urlGenerator; + private UrlGeneratorInterface $urlGenerator; /** * Create a new instance. @@ -72,24 +75,38 @@ public function __construct(Connection $connection, IFactory $factory, UrlGenera * @param GetGlobalButtonEvent $event The event. * * @return void + * + * @throws \Doctrine\DBAL\Exception */ public function getGlobalButton(GetGlobalButtonEvent $event) { - $environment = $event->getEnvironment(); - if ('addall' !== $event->getKey() - || 'tl_metamodel_dcasetting' !== $environment->getDataDefinition()->getName()) { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + 'addall' !== $event->getKey() + || 'tl_metamodel_dcasetting' !== $dataDefinition->getName() + ) { return; } - $inputScreen = ModelId::fromSerialized($environment->getInputProvider()->getParameter('pid'))->getId(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $inputScreen = ModelId::fromSerialized($inputProvider->getParameter('pid'))->getId(); $modelId = $this->connection->createQueryBuilder() ->select('d.pid') ->from('tl_metamodel_dca', 'd') ->where('d.id=:pid') ->setParameter('pid', $inputScreen) - ->execute() - ->fetchColumn(); + ->executeQuery() + ->fetchOne(); + + if (false === $modelId) { + return; + } $name = $this->factory->translateIdToMetaModelName($modelId); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AttributeOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AttributeOptionListener.php index af570933e..cea6a5956 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AttributeOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/AttributeOptionListener.php @@ -38,7 +38,7 @@ class AttributeOptionListener extends AbstractListener * * @var SelectAttributeOptionLabelFormatter */ - private $attributeLabelFormatter; + private SelectAttributeOptionLabelFormatter $labelFormatter; /** * {@inheritDoc} @@ -47,10 +47,10 @@ public function __construct( RequestScopeDeterminator $scopeDeterminator, IFactory $factory, Connection $connection, - SelectAttributeOptionLabelFormatter $attributeLabelFormatter + SelectAttributeOptionLabelFormatter $labelFormatter ) { parent::__construct($scopeDeterminator, $factory, $connection); - $this->attributeLabelFormatter = $attributeLabelFormatter; + $this->labelFormatter = $labelFormatter; } /** @@ -69,7 +69,7 @@ public function getAttributeOptions(GetPropertyOptionsEvent $event) $model = $event->getModel(); $metaModel = $this->getMetaModelFromModel($model); - if (!$metaModel) { + if (null === $metaModel) { return; } @@ -90,13 +90,13 @@ public function getAttributeOptions(GetPropertyOptionsEvent $event) ->andWhere('t.attr_id<>:id') ->setParameter('id', $attributeId); } - $alreadyTaken = $alreadyTaken->execute()->fetchAll(\PDO::FETCH_COLUMN); + $alreadyTaken = $alreadyTaken->executeQuery()->fetchFirstColumn(); foreach ($metaModel->getAttributes() as $attribute) { if ($attribute instanceof IInternal || in_array($attribute->get('id'), $alreadyTaken)) { continue; } - $options[$attribute->get('id')] = $this->attributeLabelFormatter->formatLabel($attribute); + $options[$attribute->get('id')] = $this->labelFormatter->formatLabel($attribute); } $event->setOptions($options); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/DisableMandatoryListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/DisableMandatoryListener.php index 9f41f92c5..2dded529e 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/DisableMandatoryListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/DisableMandatoryListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Ingolf Steinhardt * @author Richard Henkenjohann * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,9 +25,12 @@ use Contao\Message; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use Doctrine\DBAL\Connection; use MetaModels\IFactory; -use Symfony\Component\Translation\TranslatorInterface; +use MetaModels\IMetaModel; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This handles disabling of the mandatory field. @@ -39,7 +42,7 @@ class DisableMandatoryListener extends AbstractListener * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * Create a new instance. @@ -68,27 +71,28 @@ public function __construct( */ public function handle(BuildWidgetEvent $event) { - $environment = $event->getEnvironment(); - if (($environment->getDataDefinition()->getName() !== 'tl_metamodel_dcasetting') + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ( + ($dataDefinition->getName() !== 'tl_metamodel_dcasetting') || ($event->getProperty()->getName() !== 'mandatory') - || (null === $event->getModel()->getId())) { + || (null === $event->getModel()->getId()) + ) { return; } - $model = $event->getModel(); + $model = $event->getModel(); + assert($model instanceof ModelInterface); $metaModel = $this->getMetaModelFromModel($model); - $attribute = $metaModel->getAttributeById($model->getProperty('attr_id')); + assert($metaModel instanceof IMetaModel); + $attribute = $metaModel->getAttributeById((int) $model->getProperty('attr_id')); if (null === $attribute) { return; } if ($attribute->get('isunique')) { Message::addInfo( - $this->translator->trans( - 'tl_metamodel_dcasetting.mandatory_for_unique_attr', - [], - 'contao_tl_metamodel_dcasetting' - ) + $this->translator->trans('mandatory_for_unique_attr', [], 'tl_metamodel_dcasetting') ); $extra = $event->getProperty()->getExtra(); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/DisableReadOnlyListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/DisableReadOnlyListener.php index 28d1a16a1..78228858d 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/DisableReadOnlyListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/DisableReadOnlyListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,9 +24,12 @@ use Contao\Message; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\BuildWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use Doctrine\DBAL\Connection; use MetaModels\IFactory; -use Symfony\Component\Translation\TranslatorInterface; +use MetaModels\IMetaModel; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This handles disabling of the readonly field @@ -38,7 +41,7 @@ class DisableReadOnlyListener extends AbstractListener * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * Create a new instance. @@ -68,22 +71,29 @@ public function __construct( public function handle(BuildWidgetEvent $event) { $environment = $event->getEnvironment(); - if (($environment->getDataDefinition()->getName() !== 'tl_metamodel_dcasetting') + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ($dataDefinition->getName() !== 'tl_metamodel_dcasetting') || ($event->getProperty()->getName() !== 'readonly') - || (null === $event->getModel()->getId())) { + || (null === $event->getModel()->getId()) + ) { return; } - $model = $event->getModel(); + $model = $event->getModel(); + assert($model instanceof ModelInterface); $metaModel = $this->getMetaModelFromModel($model); + assert($metaModel instanceof IMetaModel); + $attribute = $metaModel->getAttributeById((int) $model->getProperty('attr_id')); + if (null === $attribute) { + return; + } - if ($metaModel->getAttributeById($model->getProperty('attr_id'))->get('force_alias')) { + if ($attribute->get('force_alias')) { Message::addInfo( - $this->translator->trans( - 'tl_metamodel_dcasetting.readonly_for_force_alias', - [], - 'contao_tl_metamodel_dcasetting' - ) + $this->translator->trans('readonly_for_force_alias', [], 'tl_metamodel_dcasetting') ); $extra = $event->getProperty()->getExtra(); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/EditMaskSubHeadlineListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/EditMaskSubHeadlineListener.php new file mode 100644 index 000000000..4dcb4aa8c --- /dev/null +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/EditMaskSubHeadlineListener.php @@ -0,0 +1,146 @@ + + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting; + +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetEditMaskSubHeadlineEvent; +use Contao\CoreBundle\String\SimpleTokenParser; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use MetaModels\DcGeneral\DataDefinition\Definition\IMetaModelDefinition; +use MetaModels\ViewCombination\InputScreenInformationBuilder; +use Symfony\Contracts\Translation\TranslatorInterface; + +/** + * This handles the additional part of sub-headline in input mask. + */ +final class EditMaskSubHeadlineListener +{ + /** + * The input screen information builder. + * + * @var InputScreenInformationBuilder + */ + private InputScreenInformationBuilder $inputScreens; + + /** + * The token parser. + * + * @var SimpleTokenParser + */ + private SimpleTokenParser $tokenParser; + + /** + * The translator. + * + * @var TranslatorInterface + */ + private TranslatorInterface $translator; + + /** + * EditMaskSubHeadlineListener constructor. + * + * @param InputScreenInformationBuilder $inputScreens The input screen information builder. + * @param SimpleTokenParser $tokenParser The token parser. + * @param TranslatorInterface $translator The translator. + */ + public function __construct( + InputScreenInformationBuilder $inputScreens, + SimpleTokenParser $tokenParser, + TranslatorInterface $translator + ) { + $this->inputScreens = $inputScreens; + $this->tokenParser = $tokenParser; + $this->translator = $translator; + } + + /** + * Set sub-headline. + * + * @param GetEditMaskSubHeadlineEvent $event The sub-headline event. + */ + public function __invoke(GetEditMaskSubHeadlineEvent $event): void + { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if (!\str_starts_with($dataDefinition->getName(), 'mm_')) { + return; + } + + // Nothing to do on create item. + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ('create' === $inputProvider->getParameter('act')) { + return; + } + + // Retrieve the settings of the input mask for item attribute. + $metaModel = $dataDefinition->getDefinition(IMetaModelDefinition::NAME); + assert($metaModel instanceof IMetaModelDefinition); + $metaModelName = $dataDefinition->getName(); + $screen = $this->inputScreens->fetchInputScreens([$metaModelName => $metaModel->getActiveInputScreen()]); + $screenMeta = $screen[$metaModelName]['meta'] ?? null; + + if (null === $screenMeta || null === ($headline = ($screenMeta['subheadline'] ?? null))) { + return; + } + + $tokenData = []; + // Get model properties. + foreach ($event->getModel()->getPropertiesAsArray() as $keyData => $valueData) { + $tokenData['model_' . $keyData] = $valueData; + } + + // Replace simple tokens. + $headlineAdd = $this->replaceSimpleTokensAtHeadline($headline, $tokenData); + + // Translate language key and add headline part. + $subHeadline = + $this->translator->trans('editRecord', ['%item%' => $headlineAdd], $metaModelName); + + $event->setHeadline($subHeadline); + } + + /** + * Replace simple tokens at headline parameter. + * + * @param string $headline The headline string. + * @param array $tokenData The token data. + * + * @return string + */ + private function replaceSimpleTokensAtHeadline(string $headline, array $tokenData): string + { + if ( + \str_contains($headline, '##') + || \str_contains($headline, '##') + ) { + $headline = + $this->tokenParser->parse( + \str_replace('#', '#', $headline), + $tokenData, + false + ); + } + + return $headline; + } +} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/LegendTitleListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/LegendTitleListener.php index 8fad10876..81320f09c 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/LegendTitleListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/LegendTitleListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,7 +27,9 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use MetaModels\Dca\Helper; +use MetaModels\IMetaModel; /** * This handles the serialization and deserialization as well as the building of the title widget. @@ -47,10 +50,11 @@ public function decodeValue(DecodePropertyValueForWidgetEvent $event) } $metaModel = $this->getMetaModelFromModel($event->getModel()); + assert($metaModel instanceof IMetaModel); $values = Helper::decodeLangArray($event->getValue(), $metaModel); - $event->setValue(unserialize($values)); + $event->setValue(\unserialize($values)); } /** @@ -67,6 +71,7 @@ public function encodeValue(EncodePropertyValueFromWidgetEvent $event) } $metaModel = $this->getMetaModelFromModel($event->getModel()); + assert($metaModel instanceof IMetaModel); $values = Helper::encodeLangArray($event->getValue(), $metaModel); @@ -87,13 +92,16 @@ public function buildWidget(BuildWidgetEvent $event) } $metaModel = $this->getMetaModelFromModel($event->getModel()); + assert($metaModel instanceof IMetaModel); + $translator = $event->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); Helper::prepareLanguageAwareWidget( $event->getEnvironment(), $event->getProperty(), $metaModel, - $event->getEnvironment()->getTranslator()->translate('name_langcode', 'tl_metamodel_dcasetting'), - $event->getEnvironment()->getTranslator()->translate('name_value', 'tl_metamodel_dcasetting'), + $translator->translate('name_langcode', 'tl_metamodel_dcasetting'), + $translator->translate('name_value', 'tl_metamodel_dcasetting'), false, StringUtil::deserialize($event->getModel()->getProperty('legendtitle'), true) ); @@ -107,10 +115,10 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) if (!parent::wantToHandle($event)) { return false; } - if (method_exists($event, 'getPropertyName') && ('legendtitle' !== $event->getPropertyName())) { + if (\method_exists($event, 'getPropertyName') && ('legendtitle' !== $event->getPropertyName())) { return false; } - if (method_exists($event, 'getProperty')) { + if (\method_exists($event, 'getProperty')) { $property = $event->getProperty(); if ($property instanceof PropertyInterface) { $property = $property->getName(); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/ManipulateWidgetListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/ManipulateWidgetListener.php new file mode 100644 index 000000000..ca04f4432 --- /dev/null +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/ManipulateWidgetListener.php @@ -0,0 +1,112 @@ + + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting; + +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ManipulateWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; +use MetaModels\Attribute\IInternal; +use MetaModels\DcGeneral\Data\Model; +use MetaModels\IItem; + +final class ManipulateWidgetListener +{ + /** + * The scope determinator. + * + * @var RequestScopeDeterminator + */ + private RequestScopeDeterminator $scopeDeterminator; + + /** + * Create a new instance. + * + * @param RequestScopeDeterminator $scopeDeterminator The scope determinator. + */ + public function __construct( + RequestScopeDeterminator $scopeDeterminator + ) { + $this->scopeDeterminator = $scopeDeterminator; + } + /** + * Change the widget template with your own choice. + * + * @param ManipulateWidgetEvent $event The event. + * + * @return void + */ + public function handle(ManipulateWidgetEvent $event) + { + if (!$this->wantToHandle($event)) { + return; + } + + $model = $event->getModel(); + if (!$model instanceof Model) { + return; + } + + $property = $event->getProperty(); + $item = $model->getItem(); + assert($item instanceof IItem); + if (null === $attribute = $item->getMetaModel()->getAttribute($property->getName())) { + return; + } + + // Check virtual types. + if ($attribute instanceof IInternal) { + return; + } + + if (!\in_array('be_template', $attribute->getAttributeSettingNames(), true)) { + return; + } + + $propExtra = $property->getExtra(); + + if (null !== ($template = $propExtra['be_template'] ?? null)) { + $event->getWidget()->template = $template; + } + } + + /** + * Test if the event is for the correct table and in backend scope. + * + * @param AbstractEnvironmentAwareEvent $event The event to test. + * + * @return bool + */ + protected function wantToHandle(AbstractEnvironmentAwareEvent $event): bool + { + if (!$this->scopeDeterminator->currentScopeIsBackend()) { + return false; + } + + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if (!\str_starts_with($dataDefinition->getName(), 'mm_')) { + return false; + } + + return true; + } +} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/ModelToLabelListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/ModelToLabelListener.php index 7b4cfd9f8..16e7e87ba 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/ModelToLabelListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/ModelToLabelListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,7 +28,8 @@ use MetaModels\Attribute\IAttributeFactory; use MetaModels\CoreBundle\Assets\IconBuilder; use MetaModels\IFactory; -use Symfony\Component\Translation\TranslatorInterface; +use MetaModels\IMetaModel; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This handles the rendering of models to labels. @@ -40,21 +41,21 @@ class ModelToLabelListener extends AbstractListener * * @var IAttributeFactory */ - private $attributeFactory; + private IAttributeFactory $attributeFactory; /** * The icon builder. * * @var IconBuilder */ - private $iconBuilder; + private IconBuilder $iconBuilder; /** * The translator. * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * Create a new instance. @@ -120,7 +121,8 @@ private function drawAttribute(ModelToLabelEvent $event) { $model = $event->getModel(); $metaModel = $this->getMetaModelFromModel($model); - $attribute = $metaModel->getAttributeById($model->getProperty('attr_id')); + assert($metaModel instanceof IMetaModel); + $attribute = $metaModel->getAttributeById((int) $model->getProperty('attr_id')); if ($attribute) { $type = $attribute->get('type'); @@ -133,7 +135,7 @@ private function drawAttribute(ModelToLabelEvent $event) $variant = ($metaModel->hasVariants() && $attribute->get('isvariant')) ? ', variant' : ''; $name = $attribute->getName(); $colName = $attribute->getColName(); - $isUnique = $attribute->get('isunique'); + $isUnique = (bool) $attribute->get('isunique'); } else { $type = 'unknown ID: ' . $model->getProperty('attr_id'); $image = $this->iconBuilder->getBackendIconImageTag('bundles/metamodelscore/images/icons/fields.png'); @@ -143,6 +145,7 @@ private function drawAttribute(ModelToLabelEvent $event) $isUnique = false; } + /** @psalm-suppress InvalidArgument */ $event ->setLabel('
%s [%s%s]
@@ -155,11 +158,11 @@ private function drawAttribute(ModelToLabelEvent $event) $variant, $image, $name, - // unique attributes are automatically mandatory - $model->getProperty('mandatory') || $isUnique - ? ' ['. $this->trans('mandatory.0') . ']' + // unique attributes are automatically mandatory. + (bool) $model->getProperty('mandatory') || $isUnique + ? ' [' . $this->trans('mandatory.label') . ']' : '', - $model->getProperty('tl_class') ? sprintf('[%s]', $model->getProperty('tl_class')) : '' + $model->getProperty('tl_class') ? \sprintf('[%s]', $model->getProperty('tl_class')) : '' ]); } @@ -174,24 +177,27 @@ private function drawLegend(ModelToLabelEvent $event) { $model = $event->getModel(); $metaModel = $this->getMetaModelFromModel($model); - if (is_array($legend = StringUtil::deserialize($model->getProperty('legendtitle')))) { + assert($metaModel instanceof IMetaModel); + if (\is_array($legend = StringUtil::deserialize($model->getProperty('legendtitle')))) { + /** @psalm-suppress DeprecatedMethod */ foreach ([$metaModel->getActiveLanguage(), $metaModel->getFallbackLanguage()] as $language) { - if (array_key_exists($language, $legend) && !empty($legend[$language])) { + if (\array_key_exists($language ?? '', $legend) && !empty($legend[$language])) { $legend = $legend[$language]; break; } } } - if (empty($legend)) { + if (null === $legend) { $legend = 'legend'; } + /** @psalm-suppress InvalidArgument */ $event ->setLabel('
%s
%s%s
') ->setArgs([ $model->getProperty('published') ? 'published' : 'unpublished', - $this->trans('dcatypes.legend'), + $this->translator->trans('dcatypes.legend', [], 'tl_metamodel_dcasetting'), $legend, $model->getProperty('legendhide') ? ':hide' : '' ]); @@ -206,6 +212,6 @@ private function drawLegend(ModelToLabelEvent $event) */ private function trans($key) { - return $this->translator->trans('tl_metamodel_dcasetting.' . $key, [], 'contao_tl_metamodel_dcasetting'); + return $this->translator->trans($key, [], 'tl_metamodel_dcasetting'); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/RemoveOverrideButtonListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/RemoveOverrideButtonListener.php index 88ebabe59..41104d444 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/RemoveOverrideButtonListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/RemoveOverrideButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/core * @author Christian Schiffler - * @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 */ @@ -20,6 +21,8 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetSelectModeButtonsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; /** * This class takes care of removing the override button when select mode is active. @@ -35,8 +38,14 @@ class RemoveOverrideButtonListener */ public function removeButton(GetSelectModeButtonsEvent $event) { - if (('tl_metamodel_dcasetting' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('select' !== $event->getEnvironment()->getInputProvider()->getParameter('act')) + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + if ( + ('tl_metamodel_dcasetting' !== $dataDefinition->getName()) + || ('select' !== $inputProvider->getParameter('act')) ) { return; } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/SetVisibilityConditionIconListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/SetVisibilityConditionIconListener.php index bb4e9caf9..b9374b923 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/SetVisibilityConditionIconListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/SetVisibilityConditionIconListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package MetaModels/core * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,12 +21,13 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; -use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use Contao\Image; use Contao\StringUtil; -use Doctrine\DBAL\Connection; -use MetaModels\IFactory; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This handles the icon as status of visibility condition. @@ -42,24 +43,29 @@ class SetVisibilityConditionIconListener extends AbstractListener */ public function handle(GetOperationButtonEvent $event): void { - $environment = $event->getEnvironment(); - if ('tl_metamodel_dcasetting' !== $environment->getDataDefinition()->getName() - || 'conditions' !== $event->getCommand()->getName()) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + $command = $event->getCommand(); + assert($command instanceof CommandInterface); + if ( + 'tl_metamodel_dcasetting' !== $dataDefinition->getName() + || 'conditions' !== $command->getName() + ) { return; } - + $model = $event->getModel(); + assert($model instanceof ModelInterface); $statement = $this->connection->createQueryBuilder() ->select('count(*) as count') ->from('tl_metamodel_dcasetting_condition', 't') ->where('settingId=:settingId') - ->setParameter('settingId', $event->getModel()->getId()) + ->setParameter('settingId', $model->getId()) ->setMaxResults(1) - ->execute() + ->executeQuery() ->fetchFirstColumn(); - $command = $event->getCommand(); $extra = (array) $command->getExtra(); - $icon = $extra['icon']; + $icon = (string) $extra['icon']; if (empty($statement[0])) { $iconDisabledSuffix = '_1'; @@ -67,17 +73,17 @@ public function handle(GetOperationButtonEvent $event): void if ($icon !== Image::getPath($icon)) { $iconDisabledSuffix = '_'; } - $icon = \substr_replace($icon, $iconDisabledSuffix, \strrpos($icon, '.'), 0); + $icon = \substr_replace($icon, $iconDisabledSuffix, (int) \strrpos($icon, '.'), 0); } $button = \sprintf( ' %s', $command->getName(), - $event->getHref(), + $event->getHref() ?? '', StringUtil::specialchars( - \sprintf((string) $command->getDescription(), $event->getModel()->getID()) + \sprintf($event->getTitle(), $model->getID()) ), - $this->renderImageAsHtml($event, $icon, $command->getLabel()) + $this->renderImageAsHtml($event, $icon, $event->getLabel()) ); $event->setHtml($button); @@ -94,12 +100,14 @@ public function handle(GetOperationButtonEvent $event): void */ private function renderImageAsHtml(GetOperationButtonEvent $event, string $src, string $alt): string { + $dispatcher = $event->getEnvironment()->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); /** @var GenerateHtmlEvent $imageEvent */ - $imageEvent = $event->getEnvironment()->getEventDispatcher()->dispatch( + $imageEvent = $dispatcher->dispatch( new GenerateHtmlEvent($src, $alt), ContaoEvents::IMAGE_GET_HTML ); - return $imageEvent->getHtml(); + return $imageEvent->getHtml() ?? ''; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/TemplateOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/TemplateOptionListener.php new file mode 100644 index 000000000..c0c4a4649 --- /dev/null +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSetting/TemplateOptionListener.php @@ -0,0 +1,99 @@ + + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting; + +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; +use MetaModels\BackendIntegration\TemplateList; + +/** + * This handles the providing of available templates. + */ +class TemplateOptionListener +{ + /** + * The scope determinator. + * + * @var RequestScopeDeterminator + */ + private RequestScopeDeterminator $scopeDeterminator; + + /** + * The template list provider. + * + * @var TemplateList + */ + private TemplateList $templateList; + + /** + * Create a new instance. + * + * @param RequestScopeDeterminator $scopeDeterminator The scope determinator. + * @param TemplateList $templateList The template list provider. + */ + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + TemplateList $templateList + ) { + $this->scopeDeterminator = $scopeDeterminator; + $this->templateList = $templateList; + } + + /** + * Retrieve the options for the frontend widget template. + * + * @param GetPropertyOptionsEvent $event The event. + * + * @return void + */ + public function handle(GetPropertyOptionsEvent $event): void + { + if (!$this->wantToHandle($event) || ($event->getPropertyName() !== 'be_template')) { + return; + } + + $event->setOptions($this->templateList->getTemplatesForBase('be_widget')); + } + + /** + * Test if the event is for the correct table and in backend scope. + * + * @param AbstractEnvironmentAwareEvent $event The event to test. + * + * @return bool + */ + protected function wantToHandle(AbstractEnvironmentAwareEvent $event): bool + { + if (!$this->scopeDeterminator->currentScopeIsBackend()) { + return false; + } + + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ('tl_metamodel_dcasetting' !== $dataDefinition->getName()) { + return false; + } + + return true; + } +} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/AbstractListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/AbstractListener.php index 08751fc77..c8340df46 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/AbstractListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/AbstractListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,11 +23,15 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\IFactory; +use MetaModels\IMetaModel; /** * This provides a way to obtain a MetaModel. @@ -39,7 +43,7 @@ abstract class AbstractListener * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * The MetaModel factory. @@ -53,7 +57,7 @@ abstract class AbstractListener * * @var Connection */ - private $connection; + private Connection $connection; /** * Create a new instance. @@ -77,23 +81,31 @@ public function __construct( * * @param EnvironmentInterface $interface The environment. * - * @return \MetaModels\IMetaModel + * @return IMetaModel * - * @throws \RuntimeException Throws if could not retrieve metamodel. + * @throws \RuntimeException Throws if you could not retrieve metamodel. + * @throws Exception */ public function getMetaModel(EnvironmentInterface $interface) { + $inputProvider = $interface->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + $metaModelId = $this->connection ->createQueryBuilder() ->select('d.pid') ->from('tl_metamodel_dca', 'd') ->leftJoin('d', 'tl_metamodel_dcasetting', 's', 'd.id=s.pid') ->where('s.id=:id') - ->setParameter('id', ModelId::fromSerialized($interface->getInputProvider()->getParameter('pid'))->getId()) - ->execute(); + ->setParameter('id', ModelId::fromSerialized($inputProvider->getParameter('pid'))->getId()) + ->executeQuery(); - if ($tableName = $this->factory->translateIdToMetaModelName($metaModelId = $metaModelId->fetchColumn())) { - return $this->factory->getMetaModel($tableName); + if ( + false !== ($metaModelId = $metaModelId->fetchOne()) + && ($tableName = $this->factory->translateIdToMetaModelName($metaModelId)) + && null !== ($metaModel = $this->factory->getMetaModel($tableName)) + ) { + return $metaModel; } throw new \RuntimeException('Could not retrieve MetaModel ' . $metaModelId); @@ -112,13 +124,15 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) return false; } - $environment = $event->getEnvironment(); - if ('tl_metamodel_dcasetting_condition' !== $environment->getDataDefinition()->getName()) { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ('tl_metamodel_dcasetting_condition' !== $dataDefinition->getName()) { return false; } if ($event instanceof AbstractModelAwareEvent) { - if ($event->getEnvironment()->getDataDefinition()->getName() !== $event->getModel()->getProviderName()) { + if ($dataDefinition->getName() !== $event->getModel()->getProviderName()) { return false; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/AttributeIdListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/AttributeIdListener.php index 8ccf9524c..2a554b924 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/AttributeIdListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/AttributeIdListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,6 +27,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; use Doctrine\DBAL\Connection; +use MetaModels\Attribute\IAttribute; use MetaModels\CoreBundle\DcGeneral\PropertyConditionFactory; use MetaModels\CoreBundle\Formatter\SelectAttributeOptionLabelFormatter; use MetaModels\IFactory; @@ -41,7 +42,7 @@ class AttributeIdListener extends AbstractConditionFactoryUsingListener * * @var SelectAttributeOptionLabelFormatter */ - private $attributeLabelFormatter; + private SelectAttributeOptionLabelFormatter $labelFormatter; /** * {@inheritDoc} @@ -51,10 +52,10 @@ public function __construct( IFactory $factory, Connection $connection, PropertyConditionFactory $conditionFactory, - SelectAttributeOptionLabelFormatter $attributeLabelFormatter + SelectAttributeOptionLabelFormatter $labelFormatter ) { parent::__construct($scopeDeterminator, $factory, $connection, $conditionFactory); - $this->attributeLabelFormatter = $attributeLabelFormatter; + $this->labelFormatter = $labelFormatter; } /** @@ -81,8 +82,8 @@ public function getAttributeOptions(GetPropertyOptionsEvent $event) } $colName = $attribute->getColName(); - $strSelectVal = $metaModel->getTableName() .'_' . $colName; - $result[$strSelectVal] = $this->attributeLabelFormatter->formatLabel($attribute); + $strSelectVal = $metaModel->getTableName() . '_' . $colName; + $result[$strSelectVal] = $this->labelFormatter->formatLabel($attribute); } $event->setOptions($result); @@ -104,19 +105,19 @@ public function decodeAttributeValue(DecodePropertyValueForWidgetEvent $event) $metaModel = $this->getMetaModel($event->getEnvironment()); $value = $event->getValue(); - if (!($metaModel && $value)) { + if (!$value) { $event->setValue(null); return; } - $attribute = $metaModel->getAttributeById($value); + $attribute = $metaModel->getAttributeById((int) $value); if ($attribute) { - $event->setValue($metaModel->getTableName() .'_' . $attribute->getColName()); + $event->setValue($metaModel->getTableName() . '_' . $attribute->getColName()); } } /** - * Translates an generated alias to the corresponding attribute id. + * Translates a generated alias to the corresponding attribute id. * * @param EncodePropertyValueFromWidgetEvent $event The event. * @@ -131,18 +132,17 @@ public function encodeAttributeValue(EncodePropertyValueFromWidgetEvent $event) $metaModel = $this->getMetaModel($event->getEnvironment()); $value = $event->getValue(); - if (!($metaModel && $value)) { + if (!$value) { return; } // Cut off the 'mm_xyz_' prefix. - $value = substr($value, \strlen($metaModel->getTableName() . '_')); + $value = \substr($value, \strlen($metaModel->getTableName() . '_')); $attribute = $metaModel->getAttribute($value); + assert($attribute instanceof IAttribute); - if ($attribute) { - $event->setValue($attribute->get('id')); - } + $event->setValue($attribute->get('id')); } /** @@ -153,10 +153,10 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) if (!parent::wantToHandle($event)) { return false; } - if (method_exists($event, 'getPropertyName') && ('attr_id' !== $event->getPropertyName())) { + if (\method_exists($event, 'getPropertyName') && ('attr_id' !== $event->getPropertyName())) { return false; } - if (method_exists($event, 'getProperty') && ('attr_id' !== $event->getProperty())) { + if (\method_exists($event, 'getProperty') && ('attr_id' !== $event->getProperty())) { return false; } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/ModelToLabelListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/ModelToLabelListener.php index d2324c473..0d3cf22f8 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/ModelToLabelListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/ModelToLabelListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,10 +24,11 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use Doctrine\DBAL\Connection; use MetaModels\CoreBundle\Assets\IconBuilder; use MetaModels\IFactory; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This handles the rendering of models to labels. @@ -38,14 +40,14 @@ class ModelToLabelListener extends AbstractListener * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * The icon builder. * * @var IconBuilder */ - private $iconBuilder; + private IconBuilder $iconBuilder; /** * Create a new instance. @@ -87,31 +89,32 @@ public function handle(ModelToLabelEvent $event) $environment = $event->getEnvironment(); $model = $event->getModel(); $metaModel = $this->getMetaModel($environment); - $attribute = $metaModel->getAttributeById($model->getProperty('attr_id')); + $attribute = $metaModel->getAttributeById((int) $model->getProperty('attr_id')); $type = $model->getProperty('type'); $parameterValue = (\is_array($model->getProperty('value')) - ? implode(', ', $model->getProperty('value')) - : $model->getProperty('value')); - - $name = $this->translator->trans( - 'tl_metamodel_dcasetting_condition.conditionnames.' . $type, - [], - 'contao_tl_metamodel_dcasetting_condition' + ? \implode(', ', $model->getProperty('value')) + : $model->getProperty('value') ); - $event - ->setLabel($this->getLabelText($type)) - ->setArgs([ - $this->iconBuilder->getBackendIconImageTag( - 'bundles/metamodelscore/images/icons/filter_default.png', - $name, - '', - 'bundles/metamodelscore/images/icons/filter_default.png' - ), + $name = $this->translator->trans('conditionnames.' . $type, [], 'tl_metamodel_dcasetting_condition'); + + $params = [ + '%icon%' => $this->iconBuilder->getBackendIconImageTag( + 'bundles/metamodelscore/images/icons/filter_default.png', $name, - $attribute ? $attribute->getName() : '' . $model->getProperty('attr_id'), - $parameterValue - ]); + '', + 'bundles/metamodelscore/images/icons/filter_default.png' + ), + '%name%' => $name, + '%attribute%' => $attribute ? $attribute->getName() : '' . $model->getProperty('attr_id'), + '%value%' => $parameterValue, + '%comment%' => '' !== ($comment = $model->getProperty('comment')) ? '
' . $comment : '', + ]; + + /** @psalm-suppress InvalidArgument */ + $event + ->setLabel($this->getLabelText($type, $params)) + ->setArgs(array_values($params)); } /** @@ -119,9 +122,12 @@ public function handle(ModelToLabelEvent $event) */ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) { - return $event->getEnvironment()->getInputProvider()->hasParameter('mode') + $inputProvider = $event->getEnvironment()->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + return $inputProvider->hasParameter('mode') ? parent::wantToHandle($event) - && ('select' === $event->getEnvironment()->getInputProvider()->getParameter('act')) + && ('select' === $inputProvider->getParameter('act')) : parent::wantToHandle( $event ); @@ -130,27 +136,22 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) /** * Retrieve the label text for a condition setting or the default one. * - * @param string $type The type of the element. + * @param string $type The type of the element. + * @param array $params The params. * * @return string */ - private function getLabelText($type) + private function getLabelText(string $type, array $params): string { - $label = $this->translator->trans( - 'tl_metamodel_dcasetting_condition.typedesc.' . $type, - [], - 'contao_tl_metamodel_dcasetting_condition' - ); - if ($label === 'tl_metamodel_dcasetting_condition.typedesc.' . $type) { - $label = $this->translator->trans( - 'tl_metamodel_dcasetting_condition.typedesc._default_', - [], - 'contao_tl_metamodel_dcasetting_condition' - ); - if ($label === 'tl_metamodel_dcasetting_condition.typedesc._default_') { + $label = $this->translator->trans('typedesc.' . $type, $params, 'tl_metamodel_dcasetting_condition'); + + if ($label === 'typedesc.' . $type) { + $label = $this->translator->trans('typedesc._default_', $params, 'tl_metamodel_dcasetting_condition'); + if ($label === 'typedesc._default_') { return $type; } } + return $label; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/PasteButtonListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/PasteButtonListener.php index 3868c9229..6866bea99 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/PasteButtonListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/PasteButtonListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,14 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSettingCondition; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteButtonEvent; @@ -28,7 +29,8 @@ use ContaoCommunityAlliance\DcGeneral\Controller\RelationshipManager; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; -use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\BasicDefinitionInterface; /** * This handles the type options for conditions. @@ -50,11 +52,15 @@ public function handle(GetPasteButtonEvent $event) $environment = $event->getEnvironment(); $model = $event->getModel(); - $clipboard = $environment->getClipboard(); + assert($model instanceof ModelInterface); + $clipboard = $environment->getClipboard(); + assert($clipboard instanceof ClipboardInterface); // Disable all buttons if there is a circular reference. - if ($clipboard->fetch( - Filter::create()->andActionIs(ItemInterface::CUT)->andModelIs(ModelId::fromModel($model)) - )) { + if ( + $clipboard->fetch( + Filter::create()->andActionIs(ItemInterface::CUT)->andModelIs(ModelId::fromModel($model)) + ) + ) { $event ->setPasteAfterDisabled(true) ->setPasteIntoDisabled(true); @@ -87,15 +93,19 @@ public function handle(GetPasteButtonEvent $event) */ private function testParent(ModelInterface $model, GetPasteButtonEvent $event): void { - $environment = $event->getEnvironment(); - $definition = $environment->getDataDefinition(); - $mode = $definition->getBasicDefinition()->getMode(); + $environment = $event->getEnvironment(); + $definition = $environment->getDataDefinition(); + assert($definition instanceof ContainerInterface); + + $mode = $definition->getBasicDefinition()->getMode() ?? BasicDefinitionInterface::MODE_FLAT; $relationships = new RelationshipManager($definition->getModelRelationshipDefinition(), $mode); $collector = new ModelCollector($environment); - if (!$relationships->isRoot($model) + if ( + !$relationships->isRoot($model) && ($parent = $collector->searchParentOf($model)) - && !$this->acceptsAnotherChild($parent, $collector)) { + && !$this->acceptsAnotherChild($parent, $collector) + ) { $event->setPasteAfterDisabled(true); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/TypeOptionsListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/TypeOptionsListener.php index d3bc07d08..d8baa556c 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/TypeOptionsListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/TypeOptionsListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,7 +27,7 @@ use Doctrine\DBAL\Connection; use MetaModels\CoreBundle\DcGeneral\PropertyConditionFactory; use MetaModels\IFactory; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This handles the type options for conditions. @@ -38,7 +39,7 @@ class TypeOptionsListener extends AbstractConditionFactoryUsingListener * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * Create a new instance. @@ -78,11 +79,8 @@ public function handle(GetPropertyOptionsEvent $event) $options = []; foreach ($this->conditionFactory->getTypeNames() as $condition) { - $options[$condition] = $this->translator->trans( - 'tl_metamodel_dcasetting_condition.conditionnames.' . $condition, - [], - 'contao_tl_metamodel_dcasetting_condition' - ); + $options[$condition] = + $this->translator->trans('conditionnames.' . $condition, [], 'tl_metamodel_dcasetting_condition'); } $event->setOptions($options); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/ValueListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/ValueListener.php index 5d203174f..3ddc2a5d5 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/ValueListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSettingCondition/ValueListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author Stefan Heimes - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +26,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ManipulateWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; @@ -34,9 +35,12 @@ use MetaModels\Attribute\IAttribute; use MetaModels\IMetaModel; use MetaModels\ITranslatedMetaModel; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This handles the rendering of models to labels. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class ValueListener extends AbstractListener { @@ -62,12 +66,11 @@ public function getValueOptions(GetPropertyOptionsEvent $event) return; } - $attribute = $metaModel->getAttributeById($attributeId); - + $attribute = $metaModel->getAttributeById((int) $attributeId); if ($attribute) { $options = $this->getOptionsViaDcGeneral($metaModel, $event->getEnvironment(), $attribute); $mangled = []; - foreach ((array) $options as $key => $option) { + foreach ($options as $key => $option) { $mangled['value_' . $key] = $option; } @@ -94,10 +97,14 @@ public function decodeValue(DecodePropertyValueForWidgetEvent $event) if (null === $attributeId = $model->getProperty('attr_id')) { return; } - $attribute = $metaModel->getAttributeById($attributeId); + + if (null === $attribute = $metaModel->getAttributeById((int) $attributeId)) { + return; + } + $currentLanguage = $this->extractCurrentLanguageContext($metaModel); - if (is_array($event->getValue())) { + if (\is_array($event->getValue())) { $values = []; foreach ($event->getValue() as $value) { @@ -129,10 +136,14 @@ public function encodeValue(EncodePropertyValueFromWidgetEvent $event) if (null === $attributeId = $model->getPropertyValue('attr_id')) { return; } - $attribute = $metaModel->getAttributeById($attributeId); + + if (null === $attribute = $metaModel->getAttributeById((int) $attributeId)) { + return; + } + $currentLanguage = $this->extractCurrentLanguageContext($metaModel); - if (is_array($event->getValue())) { + if (\is_array($event->getValue())) { $values = []; foreach ($event->getValue() as $value) { @@ -163,9 +174,9 @@ public function setValueOptionsMultiple(ManipulateWidgetEvent $event) } $metaModel = $this->getMetaModel($event->getEnvironment()); - $attribute = $metaModel->getAttributeById($event->getModel()->getProperty('attr_id')); + $attribute = $metaModel->getAttributeById((int) $event->getModel()->getProperty('attr_id')); - if (!($attribute && ($attribute->get('type') == 'tags'))) { + if (!($attribute && ($attribute->get('type') === 'tags'))) { return; } @@ -180,14 +191,16 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) if (!parent::wantToHandle($event)) { return false; } - if (method_exists($event, 'getPropertyName') && ('value' !== $event->getPropertyName())) { + if (\method_exists($event, 'getPropertyName') && ('value' !== $event->getPropertyName())) { return false; } - if (method_exists($event, 'getProperty')) { + + if (\method_exists($event, 'getProperty')) { $property = $event->getProperty(); if ($property instanceof PropertyInterface) { $property = $property->getName(); } + if ('value' !== $property) { return false; } @@ -211,12 +224,16 @@ private function getOptionsViaDcGeneral($metaModel, $environment, $attribute) ->setContainerName($metaModel->getTableName()); $dcGeneral = $factory->createDcGeneral(); - $subEnv = $dcGeneral->getEnvironment(); - $optEv = new GetPropertyOptionsEvent($subEnv, $subEnv->getDataProvider()->getEmptyModel()); + $subEnv = $dcGeneral->getEnvironment(); + $dataProvider = $subEnv->getDataProvider(); + assert($dataProvider instanceof DataProviderInterface); + $optEv = new GetPropertyOptionsEvent($subEnv, $dataProvider->getEmptyModel()); $optEv->setPropertyName($attribute->getColName()); - $subEnv->getEventDispatcher()->dispatch($optEv, GetPropertyOptionsEvent::NAME); + $dispatcher = $subEnv->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($optEv, GetPropertyOptionsEvent::NAME); - return $optEv->getOptions(); + return $optEv->getOptions() ?? []; } /** @@ -232,13 +249,13 @@ private function getOptionsViaDcGeneral($metaModel, $environment, $attribute) private function aliasToId(string $alias, IAttribute $attribute, string $language): string { if ($attribute instanceof IAliasConverter) { - $idForAlias = $attribute->getIdForAlias(substr($alias, 6), $language); + $idForAlias = $attribute->getIdForAlias(\substr($alias, 6), $language); if ($idForAlias !== null) { return $idForAlias; } } - return substr($alias, 6); + return \substr($alias, 6); } /** @@ -253,8 +270,8 @@ private function aliasToId(string $alias, IAttribute $attribute, string $languag */ private function idToAlias(string $idValue, IAttribute $attribute, string $language): string { - if (substr($idValue, 0, 6) == 'value_') { - $idValue = substr($idValue, 6); + if (\str_starts_with($idValue, 'value_')) { + $idValue = \substr($idValue, 6); } if ($attribute instanceof IAliasConverter) { @@ -270,7 +287,7 @@ private function idToAlias(string $idValue, IAttribute $attribute, string $langu /** * Try to find the right language context. * - * @param \MetaModels\IMetaModel $metaModel The current metamodel for the context. + * @param IMetaModel $metaModel The current metamodel for the context. * * @return string * @@ -282,10 +299,16 @@ private function extractCurrentLanguageContext(IMetaModel $metaModel): string return $metaModel->getLanguage(); } // Legacy compatibility fallback for translated metamodels not implementing the interface. + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ if ($metaModel->isTranslated(false)) { + /** @psalm-suppress DeprecatedMethod */ return $metaModel->getActiveLanguage(); } + // Use the current backend language then. - return \str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); + return \str_replace('-', '_', (string) $GLOBALS['TL_LANGUAGE']); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AbstractAbstainingListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AbstractAbstainingListener.php index 86d63ae50..b3196517c 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AbstractAbstainingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AbstractAbstainingListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -21,6 +22,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSortGroup; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; @@ -34,7 +36,7 @@ abstract class AbstractAbstainingListener * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * Create a new instance. @@ -59,13 +61,15 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) return false; } - $environment = $event->getEnvironment(); - if ('tl_metamodel_dca_sortgroup' !== $environment->getDataDefinition()->getName()) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ('tl_metamodel_dca_sortgroup' !== $dataDefinition->getName()) { return false; } if ($event instanceof AbstractModelAwareEvent) { - if ($event->getEnvironment()->getDataDefinition()->getName() !== $event->getModel()->getProviderName()) { + if ($dataDefinition->getName() !== $event->getModel()->getProviderName()) { return false; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AbstractListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AbstractListener.php index 16b3a2b5e..7665fec8c 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AbstractListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AbstractListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +25,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\IFactory; use MetaModels\IMetaModel; @@ -69,15 +70,16 @@ public function __construct( * * @param ModelInterface $model The input screen model for which to retrieve the MetaModel. * - * @return IMetaModel + * @return IMetaModel|null * * @throws DcGeneralInvalidArgumentException When an invalid model has been passed or the model does not have an id. + * @throws Exception */ protected function getMetaModelFromModel(ModelInterface $model) { - if (!(($model->getProviderName() == 'tl_metamodel_dca_sortgroup') && $model->getProperty('pid'))) { + if (!(($model->getProviderName() === 'tl_metamodel_dca_sortgroup') && $model->getProperty('pid'))) { throw new DcGeneralInvalidArgumentException( - sprintf( + \sprintf( 'Model must originate from tl_metamodel_dca_sortgroup and be saved, this one originates from %s ' . 'and has pid %s', $model->getProviderName(), @@ -92,8 +94,12 @@ protected function getMetaModelFromModel(ModelInterface $model) ->from('tl_metamodel_dca', 't') ->where('t.id=:id') ->setParameter('id', $model->getProperty('pid')) - ->execute() - ->fetchColumn(); + ->executeQuery() + ->fetchOne(); + + if (false === $metaModelId) { + return null; + } $tableName = $this->factory->translateIdToMetaModelName($metaModelId); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AttributeOptionsListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AttributeOptionsListener.php index 31ff18227..443fa685c 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AttributeOptionsListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/AttributeOptionsListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2020 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 */ @@ -26,7 +27,9 @@ use Doctrine\DBAL\Connection; use MetaModels\Attribute\IInternal; use MetaModels\CoreBundle\Formatter\SelectAttributeOptionLabelFormatter; +use MetaModels\CoreBundle\Sorter\AttributeSorter; use MetaModels\IFactory; +use MetaModels\IMetaModel; /** * This provides the attribute name options. @@ -38,21 +41,31 @@ class AttributeOptionsListener extends AbstractListener * * @var SelectAttributeOptionLabelFormatter */ - private $attributeLabelFormatter; + private SelectAttributeOptionLabelFormatter $labelFormatter; + + /** + * The attribute sorter. + * + * @var AttributeSorter + */ + private AttributeSorter $attributeSorter; /** * {@inheritDoc} * - * @param SelectAttributeOptionLabelFormatter $attributeLabelFormatter The attribute select option label formatter. + * @param SelectAttributeOptionLabelFormatter $labelFormatter The attribute select option label formatter. + * @param AttributeSorter $attributeSorter The attribute sorter. */ public function __construct( RequestScopeDeterminator $scopeDeterminator, IFactory $factory, Connection $connection, - SelectAttributeOptionLabelFormatter $attributeLabelFormatter + SelectAttributeOptionLabelFormatter $labelFormatter, + AttributeSorter $attributeSorter ) { parent::__construct($scopeDeterminator, $factory, $connection); - $this->attributeLabelFormatter = $attributeLabelFormatter; + $this->labelFormatter = $labelFormatter; + $this->attributeSorter = $attributeSorter; } /** @@ -68,14 +81,17 @@ public function handle(GetPropertyOptionsEvent $event) return; } - $result = []; - $metaModel = $this->getMetaModelFromModel($event->getModel()); + $result = []; + $metaModel = $this->getMetaModelFromModel($event->getModel()); + assert($metaModel instanceof IMetaModel); + $attributes = $metaModel->getAttributes(); + $attributes = $this->attributeSorter->sortByName($attributes); - foreach ($metaModel->getAttributes() as $attribute) { + foreach ($attributes as $attribute) { if ($attribute instanceof IInternal) { continue; } - $result[$attribute->get('id')] = $this->attributeLabelFormatter->formatLabel($attribute); + $result[$attribute->get('id')] = $this->labelFormatter->formatLabel($attribute); } $event->setOptions($result); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/ModelToLabelListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/ModelToLabelListener.php index ed0f058f9..4c771d4b6 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/ModelToLabelListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/ModelToLabelListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,7 +25,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; use Doctrine\DBAL\Connection; use MetaModels\IFactory; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This handles the rendering of models to labels. @@ -75,7 +76,7 @@ public function handle(ModelToLabelEvent $event) sprintf( '%s [%s]', $event->getLabel(), - $this->translator->trans('MSC.fallback', [], 'contao_default') + $this->translator->trans('fallback', [], 'metamodels_default') ) ); } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/SortGroupCreateListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/SortGroupCreateListener.php new file mode 100644 index 000000000..745eddd41 --- /dev/null +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/SortGroupCreateListener.php @@ -0,0 +1,98 @@ + + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSortGroup; + +use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\Event\PreEditModelEvent; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; + +/** + * This provides the attribute at generate. + */ +class SortGroupCreateListener +{ + /** + * Database connection. + * + * @var Connection + */ + private Connection $connection; + + /** + * SortGroupCreateListener constructor. + * + * @param Connection $connection Database connection. + */ + public function __construct( + Connection $connection + ) { + $this->connection = $connection; + } + + /** + * Set as default if first item. + * + * @param PreEditModelEvent $event The event. + * + * @return void + * + * @throws Exception + */ + public function handle(PreEditModelEvent $event): void + { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ('tl_metamodel_dca_sortgroup' !== $dataDefinition->getName()) { + return; + } + + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + if ('paste' !== $inputProvider->getParameter('act') || !($pid = $inputProvider->getParameter('pid'))) { + return; + } + + if (!($pid = ModelId::fromSerialized($pid)->getId())) { + return; + } + + // Retrieve if first item. + $statement = $this->connection->createQueryBuilder() + ->select('t.*') + ->from('tl_metamodel_dca_sortgroup', 't') + ->where('t.pid=:pid') + ->setParameter('pid', $pid) + ->executeQuery(); + + if ($statement->rowCount()) { + return; + } + + // Set 'isdefault' as checked is first item. + $model = $event->getModel(); + $model->setProperty('isdefault', 1); + } +} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/VisibilityConditionBuildingListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/VisibilityConditionBuildingListener.php index 694e3c44c..61a5a4ec2 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/VisibilityConditionBuildingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DcaSortGroup/VisibilityConditionBuildingListener.php @@ -97,10 +97,11 @@ public function handle(BuildDataDefinitionEvent $event) private function addCondition(PropertyInterface $property, ConditionInterface $condition) { $chain = $property->getVisibleCondition(); - if (!($chain + if ( + !($chain && ($chain instanceof PropertyConditionChain) && $chain->getConjunction() == PropertyConditionChain::AND_CONJUNCTION - ) + ) ) { if ($property->getVisibleCondition()) { $previous = array($property->getVisibleCondition()); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/DeleteOperationButtonListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/DeleteOperationButtonListener.php new file mode 100755 index 000000000..e72766218 --- /dev/null +++ b/src/CoreBundle/EventListener/DcGeneral/Table/DeleteOperationButtonListener.php @@ -0,0 +1,122 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table; + +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; +use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function assert; +use function in_array; +use function str_starts_with; + +/** + * This provides the attribute name options. + */ +class DeleteOperationButtonListener +{ + public function __construct( + private readonly RequestScopeDeterminator $scopeDeterminator, + private readonly TranslatorInterface $translator + ) { + } + + /** + * Update the delete button attributes for MetaModels tables. + * + * @param GetOperationButtonEvent $event The event. + * + * @return void + */ + public function handle(GetOperationButtonEvent $event) + { + if (!$this->wantToHandle($event)) { + return; + } + + $model = $event->getModel(); + assert($model instanceof ModelInterface); + + $attributes = $event->getAttributes(); + + + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $event->setAttributes( + 'data-msg-confirm="' . $this->translator->trans( + 'delete.confirm', + [ + '%id%' => $model->getID() + ], + $dataDefinition->getName() + ) . '" ' + . $attributes + ); + } + + /** + * Test if the event is for the correct table and in backend scope. + * + * @param AbstractEnvironmentAwareEvent $event The event to test. + * + * @return bool + */ + protected function wantToHandle(AbstractEnvironmentAwareEvent $event) + { + /** @var GetOperationButtonEvent $event */ + $command = $event->getCommand(); + assert($command instanceof CommandInterface); + + if ('delete' !== $command->getName()) { + return false; + } + + if (!$this->scopeDeterminator->currentScopeIsBackend()) { + return false; + } + + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + return str_starts_with($dataDefinition->getName(), 'mm_') + || in_array( + $dataDefinition->getName(), + [ + 'tl_metamodel', + 'tl_metamodel_attribute', + 'tl_metamodel_dca', + 'tl_metamodel_dca_sortgroup', + 'tl_metamodel_dcasetting', + 'tl_metamodel_dcasetting_condition', + 'tl_metamodel_filter', + 'tl_metamodel_filtersetting', + 'tl_metamodel_rendersetting', + 'tl_metamodel_rendersettings', + 'tl_metamodel_searchable_pages', + ] + ); + } +} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/AbstractFilterSettingTypeRenderer.php b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/AbstractFilterSettingTypeRenderer.php index 77096e8ad..8c9451abd 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/AbstractFilterSettingTypeRenderer.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/AbstractFilterSettingTypeRenderer.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,11 +31,14 @@ use ContaoCommunityAlliance\Translator\TranslatorInterface; use MetaModels\CoreBundle\Assets\IconBuilder; use MetaModels\Filter\Setting\IFilterSettingFactory; +use MetaModels\Filter\Setting\IFilterSettingTypeFactory; use MetaModels\IMetaModel; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * Handles rendering of model from tl_metamodel_filtersetting. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class AbstractFilterSettingTypeRenderer { @@ -44,28 +47,28 @@ abstract class AbstractFilterSettingTypeRenderer * * @var IFilterSettingFactory */ - private $factory; + private IFilterSettingFactory $factory; /** * The event dispatcher. * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * The icon builder. * * @var IconBuilder */ - private $iconBuilder; + private IconBuilder $iconBuilder; /** * Request scope determinator. * * @var RequestScopeDeterminator */ - private $scopeMatcher; + private RequestScopeDeterminator $scopeMatcher; /** * Create a new instance. @@ -102,8 +105,9 @@ public function modelToLabel(ModelToLabelEvent $event) } $model = $event->getModel(); - if (($model->getProviderName() !== 'tl_metamodel_filtersetting') - || !in_array($event->getModel()->getProperty('type'), $this->getTypes()) + if ( + ($model->getProviderName() !== 'tl_metamodel_filtersetting') + || !\in_array($event->getModel()->getProperty('type'), $this->getTypes()) ) { return; } @@ -148,11 +152,13 @@ abstract protected function getTypes(); protected function getLabelComment(ModelInterface $model, TranslatorInterface $translator) { if ($model->getProperty('comment')) { - return sprintf( - $translator->translate('typedesc._comment_', 'tl_metamodel_filtersetting'), - StringUtil::specialchars($model->getProperty('comment')) + return $translator->translate( + 'typedesc._comment_', + 'tl_metamodel_filtersetting', + ['%comment%' => StringUtil::specialchars($model->getProperty('comment'))] ); } + return ''; } @@ -166,6 +172,7 @@ protected function getLabelComment(ModelInterface $model, TranslatorInterface $t protected function getLabelImage(ModelInterface $model) { $typeFactory = $this->factory->getTypeFactory($model->getProperty('type')); + assert($typeFactory instanceof IFilterSettingTypeFactory); $image = $this->iconBuilder->getBackendIconImageTag( $this->updateImageWithDisabled($model, $typeFactory->getTypeIcon()), @@ -180,7 +187,7 @@ protected function getLabelImage(ModelInterface $model) ContaoEvents::BACKEND_ADD_TO_URL ); - return sprintf( + return \sprintf( '%s', $urlEvent->getUrl(), $image @@ -199,7 +206,7 @@ protected function getLabelText(TranslatorInterface $translator, ModelInterface { $type = $model->getProperty('type'); $label = $translator->translate('typenames.' . $type, 'tl_metamodel_filtersetting'); - if ($label == 'typenames.' . $type) { + if ($label === 'typenames.' . $type) { return $type; } return $label; @@ -216,10 +223,12 @@ protected function getLabelText(TranslatorInterface $translator, ModelInterface protected function getLabelPattern(EnvironmentInterface $environment, ModelInterface $model) { $translator = $environment->getTranslator(); - $type = $model->getProperty('type'); - $combined = 'typedesc.' . $type; + assert($translator instanceof TranslatorInterface); + + $type = $model->getProperty('type'); + $combined = 'typedesc.' . $type; - if (($resultPattern = $translator->translate($combined, 'tl_metamodel_filtersetting')) == $combined) { + if (($resultPattern = $translator->translate($combined, 'tl_metamodel_filtersetting')) === $combined) { $resultPattern = $translator->translate('typedesc._default_', 'tl_metamodel_filtersetting'); } @@ -239,8 +248,10 @@ protected function getLabelParametersWithAttributeAndUrlParam( ModelInterface $model ) { $translator = $environment->getTranslator(); - $metamodel = $this->getMetaModel($model); - $attribute = $metamodel->getAttributeById($model->getProperty('attr_id')); + assert($translator instanceof TranslatorInterface); + + $metamodel = $this->getMetaModel($model); + $attribute = $metamodel->getAttributeById((int) $model->getProperty('attr_id')); if ($attribute) { $attributeColumnName = $attribute->getColName(); @@ -253,16 +264,21 @@ protected function getLabelParametersWithAttributeAndUrlParam( return [ $this->getLabelImage($model), $this->getLabelText($translator, $model), - \sprintf( - $translator->translate('typedesc._attribute_', 'tl_metamodel_filtersetting'), - $attributeColumnName, - $attributeName + $translator->translate( + 'typedesc._attribute_', + 'tl_metamodel_filtersetting', + ['%colName%' => $attributeColumnName, '%name%' => $attributeName], ), $this->getLabelComment($model, $translator), - \sprintf( - $translator->translate('typedesc._url_', 'tl_metamodel_filtersetting'), - ($model->getProperty('urlparam') ? $model->getProperty('urlparam') : $attributeColumnName) - ) + $translator->translate( + 'typedesc._url_', + 'tl_metamodel_filtersetting', + [ + '%urlparam%' => ($model->getProperty('urlparam') + ? $model->getProperty('urlparam') + : $attributeColumnName) + ] + ), ]; } @@ -277,6 +293,7 @@ protected function getLabelParametersWithAttributeAndUrlParam( protected function getLabelParametersNormal(EnvironmentInterface $environment, ModelInterface $model) { $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); return [ $this->getLabelImage($model), diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/AttributeListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/AttributeListener.php index e9c353b78..13712e9ea 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/AttributeListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/AttributeListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2020 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +24,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use MetaModels\Attribute\IAttribute; use MetaModels\CoreBundle\Formatter\SelectAttributeOptionLabelFormatter; use MetaModels\Filter\Setting\IFilterSettingFactory; @@ -36,27 +39,27 @@ class AttributeListener * * @var IFilterSettingFactory */ - private $filterFactory; + private IFilterSettingFactory $filterFactory; /** * The attribute select option label formatter. * * @var SelectAttributeOptionLabelFormatter */ - private $attributeLabelFormatter; + private SelectAttributeOptionLabelFormatter $labelFormatter; /** * Create a new instance. * - * @param IFilterSettingFactory $filterFactory The filter setting factory. - * @param SelectAttributeOptionLabelFormatter $attributeLabelFormatter The attribute select option label formatter. + * @param IFilterSettingFactory $filterFactory The filter setting factory. + * @param SelectAttributeOptionLabelFormatter $labelFormatter The attribute select option label formatter. */ public function __construct( IFilterSettingFactory $filterFactory, - SelectAttributeOptionLabelFormatter $attributeLabelFormatter + SelectAttributeOptionLabelFormatter $labelFormatter ) { - $this->filterFactory = $filterFactory; - $this->attributeLabelFormatter = $attributeLabelFormatter; + $this->filterFactory = $filterFactory; + $this->labelFormatter = $labelFormatter; } /** @@ -66,10 +69,16 @@ public function __construct( * * @return void */ - public function getOptions(GetPropertyOptionsEvent $event) + public function getOptions(GetPropertyOptionsEvent $event): void { - if (('tl_metamodel_filtersetting' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('attr_id' !== $event->getPropertyName())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_filtersetting' !== $dataDefinition->getName()) + || ('attr_id' !== $event->getPropertyName()) + || null !== $event->getOptions() + ) { return; } @@ -84,12 +93,12 @@ public function getOptions(GetPropertyOptionsEvent $event) } foreach ($metaModel->getAttributes() as $attribute) { - if ($typeFilter && (!in_array($attribute->get('type'), $typeFilter))) { + if (null !== $typeFilter && (!\in_array((string) $attribute->get('type'), $typeFilter))) { continue; } - $strSelectVal = $metaModel->getTableName() .'_' . $attribute->getColName(); - $result[$strSelectVal] = $this->attributeLabelFormatter->formatLabel($attribute); + $strSelectVal = $metaModel->getTableName() . '_' . $attribute->getColName(); + $result[$strSelectVal] = $this->labelFormatter->formatLabel($attribute); } $event->setOptions($result); @@ -102,10 +111,15 @@ public function getOptions(GetPropertyOptionsEvent $event) * * @return void */ - public function decodeValue(DecodePropertyValueForWidgetEvent $event) + public function decodeValue(DecodePropertyValueForWidgetEvent $event): void { - if (('tl_metamodel_filtersetting' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('attr_id' !== $event->getProperty())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_filtersetting' !== $dataDefinition->getName()) + || ('attr_id' !== $event->getProperty()) + ) { return; } @@ -113,13 +127,13 @@ public function decodeValue(DecodePropertyValueForWidgetEvent $event) $metaModel = $this->filterFactory->createCollection($model->getProperty('fid'))->getMetaModel(); $value = $event->getValue(); - if (!($metaModel && $value)) { + if (!$value) { return; } - $attribute = $metaModel->getAttributeById($value); + $attribute = $metaModel->getAttributeById((int) $value); if ($attribute) { - $event->setValue($metaModel->getTableName() .'_' . $attribute->getColName()); + $event->setValue($metaModel->getTableName() . '_' . $attribute->getColName()); } } @@ -130,10 +144,15 @@ public function decodeValue(DecodePropertyValueForWidgetEvent $event) * * @return void */ - public function encodeValue(EncodePropertyValueFromWidgetEvent $event) + public function encodeValue(EncodePropertyValueFromWidgetEvent $event): void { - if (('tl_metamodel_filtersetting' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('attr_id' !== $event->getProperty())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_filtersetting' !== $dataDefinition->getName()) + || ('attr_id' !== $event->getProperty()) + ) { return; } @@ -141,16 +160,14 @@ public function encodeValue(EncodePropertyValueFromWidgetEvent $event) $metaModel = $this->filterFactory->createCollection($model->getProperty('fid'))->getMetaModel(); $value = $event->getValue(); - if (!($metaModel && $value)) { + if (!$value) { return; } - $value = substr($value, strlen($metaModel->getTableName() . '_')); + $value = \substr($value, \strlen($metaModel->getTableName() . '_')); $attribute = $metaModel->getAttribute($value); - - if ($attribute) { - $event->setValue($attribute->get('id')); - } + assert($attribute instanceof IAttribute); + $event->setValue($attribute->get('id')); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/DefaultOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/DefaultOptionListener.php index 51e6471b0..b3ee4690a 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/DefaultOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/DefaultOptionListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,6 +22,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\FilterSetting; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use MetaModels\Attribute\IAttribute; use MetaModels\Filter\Setting\IFilterSettingFactory; @@ -34,7 +36,7 @@ class DefaultOptionListener * * @var IFilterSettingFactory */ - private $filterFactory; + private IFilterSettingFactory $filterFactory; /** * Create a new instance. @@ -55,8 +57,13 @@ public function __construct(IFilterSettingFactory $filterFactory) */ public function handle(GetPropertyOptionsEvent $event) { - if (('tl_metamodel_filtersetting' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('defaultid' !== $event->getPropertyName())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_filtersetting' !== $dataDefinition->getName()) + || ('defaultid' !== $event->getPropertyName()) + ) { return; } @@ -64,22 +71,20 @@ public function handle(GetPropertyOptionsEvent $event) if (!($attributeId = $model->getProperty('attr_id'))) { return; } - if (null === $metaModel = $this->filterFactory->createCollection($model->getProperty('fid'))->getMetaModel()) { - return; - } - if (null === $attribute = $metaModel->getAttributeById($attributeId)) { + $metaModel = $this->filterFactory->createCollection($model->getProperty('fid'))->getMetaModel(); + + if (null === $attribute = $metaModel->getAttributeById((int) $attributeId)) { return; } - $event->setOptions($this->getOptions($attribute, $model->getProperty('onlyused') ? true : false)); + $event->setOptions($this->getOptions($attribute, (bool) $model->getProperty('onlyused'))); } /** * Ensure that all options have a value. * * @param IAttribute $attribute The options to be cleaned. - * * @param bool $onlyUsed Determines if only "used" values shall be returned. * * @return array @@ -90,7 +95,7 @@ private function getOptions($attribute, $onlyUsed) $options = []; foreach ($attribute->getFilterOptions(null, $onlyUsed) as $key => $value) { // Remove html/php tags. - $value = trim(strip_tags($value)); + $value = \trim(\strip_tags($value)); if (!empty($value)) { $options[$key] = $value; diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/FilterSettingTypeRendererCore.php b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/FilterSettingTypeRendererCore.php index 68b2175ac..8ee714241 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/FilterSettingTypeRendererCore.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/FilterSettingTypeRendererCore.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,7 +32,7 @@ class FilterSettingTypeRendererCore extends AbstractFilterSettingTypeRenderer /** * Retrieve the types this renderer is valid for. * - * @return array + * @return list */ protected function getTypes() { diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/PasteButtonListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/PasteButtonListener.php index 7aee7fb1d..4aa148d2e 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/PasteButtonListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/PasteButtonListener.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. @@ -13,17 +13,20 @@ * @package MetaModels/core * @author Christian Schiffler * @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\CoreBundle\EventListener\DcGeneral\Table\FilterSetting; +use ContaoCommunityAlliance\DcGeneral\Clipboard\ClipboardInterface; use ContaoCommunityAlliance\DcGeneral\Clipboard\Filter; use ContaoCommunityAlliance\DcGeneral\Clipboard\ItemInterface; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPasteButtonEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use MetaModels\Filter\Setting\IFilterSettingFactory; /** @@ -36,7 +39,7 @@ class PasteButtonListener * * @var IFilterSettingFactory */ - private $filterFactory; + private IFilterSettingFactory $filterFactory; /** * Create a new instance. @@ -58,13 +61,14 @@ public function __construct(IFilterSettingFactory $filterFactory) public function handle(GetPasteButtonEvent $event) { $model = $event->getModel(); + assert($model instanceof ModelInterface); + if (('tl_metamodel_filtersetting' !== $model->getProviderName())) { return; } - $environment = $event->getEnvironment(); - $model = $event->getModel(); - $clipboard = $environment->getClipboard(); + $clipboard = $event->getEnvironment()->getClipboard(); + assert($clipboard instanceof ClipboardInterface); $filter = Filter::create()->andModelIs(ModelId::fromModel($model))->andActionIs(ItemInterface::CUT); // Disable all buttons if there is a circular reference. diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/TemplateOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/TemplateOptionListener.php index 33ffcd049..671ac1bb2 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/TemplateOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/TemplateOptionListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -21,6 +22,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\FilterSetting; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use MetaModels\BackendIntegration\TemplateList; /** @@ -33,7 +35,7 @@ class TemplateOptionListener * * @var TemplateList */ - private $templateList; + private TemplateList $templateList; /** * Create a new instance. @@ -54,8 +56,13 @@ public function __construct(TemplateList $templateList) */ public function handle(GetPropertyOptionsEvent $event) { - if (('tl_metamodel_filtersetting' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('template' !== $event->getPropertyName())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_filtersetting' !== $dataDefinition->getName()) + || ('template' !== $event->getPropertyName()) + ) { return; } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/TypeOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/TypeOptionListener.php index 8994173d5..2a95fd3ac 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/TypeOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/FilterSetting/TypeOptionListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -21,6 +22,8 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\FilterSetting; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; use MetaModels\Filter\Setting\IFilterSettingFactory; /** @@ -33,7 +36,7 @@ class TypeOptionListener * * @var IFilterSettingFactory */ - private $filterFactory; + private IFilterSettingFactory $filterFactory; /** * Create a new instance. @@ -54,13 +57,20 @@ public function __construct(IFilterSettingFactory $filterFactory) */ public function handle(GetPropertyOptionsEvent $event) { - if (('tl_metamodel_filtersetting' !== $event->getEnvironment()->getDataDefinition()->getName()) - || ('type' !== $event->getPropertyName())) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ('tl_metamodel_filtersetting' !== $dataDefinition->getName()) + || ('type' !== $event->getPropertyName()) + ) { return; } $translator = $event->getEnvironment()->getTranslator(); - $options = []; + assert($translator instanceof TranslatorInterface); + + $options = []; foreach ($this->filterFactory->getTypeNames() as $filter) { $options[$filter] = $translator->translate('typenames.' . $filter, 'tl_metamodel_filtersetting'); } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/AbstractAbstainingListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/AbstractAbstainingListener.php index b2faa1ac2..984440359 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/AbstractAbstainingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/AbstractAbstainingListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -21,6 +22,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\MetaModel; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; @@ -34,7 +36,7 @@ abstract class AbstractAbstainingListener * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * Create a new instance. @@ -59,13 +61,16 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) return false; } - $environment = $event->getEnvironment(); - if ('tl_metamodel' !== $environment->getDataDefinition()->getName()) { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ('tl_metamodel' !== $dataDefinition->getName()) { return false; } if ($event instanceof AbstractModelAwareEvent) { - if ($event->getEnvironment()->getDataDefinition()->getName() !== $event->getModel()->getProviderName()) { + if ($dataDefinition->getName() !== $event->getModel()->getProviderName()) { return false; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/DcaCombineButtonListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/DcaCombineButtonListener.php index 319a20dc7..cdf02fee8 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/DcaCombineButtonListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/DcaCombineButtonListener.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. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Molineus * @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 */ @@ -23,6 +24,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetOperationButtonEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\View\CommandInterface; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; /** @@ -44,12 +47,17 @@ public function handle(GetOperationButtonEvent $event) } $command = $event->getCommand(); - if ($command->getName() == 'dca_combine') { + assert($command instanceof CommandInterface); + + $model = $event->getModel(); + assert($model instanceof ModelInterface); + + if ($command->getName() === 'dca_combine') { $event->setHref( - UrlBuilder::fromUrl($event->getHref()) + UrlBuilder::fromUrl($event->getHref() ?? '') ->setQueryParameter( 'id', - ModelId::fromValues('tl_metamodel_dca_combine', $event->getModel()->getId())->getSerialized() + ModelId::fromValues('tl_metamodel_dca_combine', $model->getId())->getSerialized() ) ->getUrl() ); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/LanguageOptionsListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/LanguageOptionsListener.php index 4e8943df8..9a7060f75 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/LanguageOptionsListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/LanguageOptionsListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +23,7 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use MenAtWork\MultiColumnWizardBundle\Event\GetOptionsEvent; /** @@ -35,7 +36,7 @@ class LanguageOptionsListener * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * Create a new instance. @@ -54,12 +55,13 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator) * * @return void */ - public function handle(GetOptionsEvent $event) + public function handle(GetOptionsEvent $event): void { if (!$this->wantToHandle($event)) { return; } + /** @psalm-suppress DeprecatedMethod */ $languages = System::getLanguages(); $hasTerritorySupport = (bool) $event->getModel()->getProperty('localeterritorysupport'); $languageOptions = []; @@ -73,7 +75,7 @@ public function handle(GetOptionsEvent $event) $languageOptions[$langKey] = \sprintf( '%s [%s]', $langValue, - $hasTerritory ? \substr_replace($langKey, '-', 2, 1) : $langKey + $langKey ); } @@ -87,7 +89,7 @@ public function handle(GetOptionsEvent $event) * * @return bool */ - protected function wantToHandle(GetOptionsEvent $event) + protected function wantToHandle(GetOptionsEvent $event): bool { if ($event->getOptions() !== null) { return false; @@ -97,12 +99,13 @@ protected function wantToHandle(GetOptionsEvent $event) return false; } - $environment = $event->getEnvironment(); - if ('tl_metamodel' !== $environment->getDataDefinition()->getName()) { + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ('tl_metamodel' !== $dataDefinition->getName()) { return false; } - if ($event->getEnvironment()->getDataDefinition()->getName() !== $event->getModel()->getProviderName()) { + if ($dataDefinition->getName() !== $event->getModel()->getProviderName()) { return false; } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/ModelSchemaManagerHintListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/ModelSchemaManagerHintListener.php new file mode 100644 index 000000000..9b6cfd17f --- /dev/null +++ b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/ModelSchemaManagerHintListener.php @@ -0,0 +1,66 @@ + + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\MetaModel; + +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\Event\PreEditModelEvent; +use Contao\Message; +use Symfony\Contracts\Translation\TranslatorInterface; + +class ModelSchemaManagerHintListener extends AbstractAbstainingListener +{ + /** + * The translator. + * + * @var TranslatorInterface + */ + private $translator; + + + /** + * Create a new instance. + * + * @param RequestScopeDeterminator $scopeDeterminator The scope determinator. + * @param TranslatorInterface $translator The translator. + */ + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + TranslatorInterface $translator + ) { + parent::__construct($scopeDeterminator); + $this->translator = $translator; + } + + /** + * Add hint at attribute. + * + * @param PreEditModelEvent $event The event. + * + * @return void + */ + public function handle(PreEditModelEvent $event): void + { + if (!$this->wantToHandle($event)) { + return; + } + + Message::addInfo($this->translator->trans('hint_schema_manager', [], 'tl_metamodel')); + } +} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/ModelToLabelListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/ModelToLabelListener.php index d305ac4db..33bd8edaa 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/ModelToLabelListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/ModelToLabelListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,7 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; use Doctrine\DBAL\Connection; -use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This handles the rendering of models to labels. @@ -36,14 +36,14 @@ class ModelToLabelListener extends AbstractAbstainingListener * * @var Connection */ - private $connection; + private Connection $connection; /** * The translator. * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * Create a new instance. @@ -69,8 +69,10 @@ public function __construct( * @param ModelToLabelEvent $event The event. * * @return void + * + * @throws \Doctrine\DBAL\Exception */ - public function handle(ModelToLabelEvent $event) + public function handle(ModelToLabelEvent $event): void { if (!$this->wantToHandle($event)) { return; @@ -79,31 +81,21 @@ public function handle(ModelToLabelEvent $event) $model = $event->getModel(); $tableName = $model->getProperty('tableName'); - if (!($model && !empty($tableName) && $this->connection->getSchemaManager()->tablesExist([$tableName]))) { - return; + $count = -1; + if (!empty($tableName) && $this->connection->createSchemaManager()->tablesExist([$tableName])) { + $count = $this->connection + ->createQueryBuilder() + ->select('COUNT(t.id) AS itemCount') + ->from($tableName, 't') + ->executeQuery() + ->fetchOne(); } // Keep the previous label. - $label = vsprintf($event->getLabel(), $event->getArgs()); + $label = \vsprintf($event->getLabel(), $event->getArgs()); $image = ((bool) $model->getProperty('translated')) ? 'locale.png' : 'locale_1.png'; - $count = $this->connection - ->createQueryBuilder() - ->select('COUNT(t.id) AS itemCount') - ->from($tableName, 't') - ->execute() - ->fetchColumn(); - - switch ($count) { - case 0: - $transId = 'tl_metamodel.itemFormatCount.0'; - break; - case 1: - $transId = 'tl_metamodel.itemFormatCount.1'; - break; - default: - $transId = 'tl_metamodel.itemFormatCount.2:'; - } + /** @psalm-suppress InvalidArgument */ $event ->setLabel(' @@ -115,7 +107,7 @@ public function handle(ModelToLabelEvent $event) $image, $label, $tableName, - $this->translator->trans($transId, [$count], 'contao_tl_metamodel') + $this->translator->trans('itemFormatCount.label', ['%count%' => $count], 'tl_metamodel') ]); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/TableNamePrefixingListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/TableNamePrefixingListener.php index 9857221d6..e0027934f 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/TableNamePrefixingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/TableNamePrefixingListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,10 +23,15 @@ use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; +use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use MetaModels\Exceptions\Database\InvalidTableNameException; use MetaModels\Helper\TableManipulator; +use MetaModels\IFactory; /** - * This prefixes all tables with "mm_". + * This prefixes all tables with "mm_" and check if exists. */ class TableNamePrefixingListener extends AbstractAbstainingListener { @@ -34,18 +40,30 @@ class TableNamePrefixingListener extends AbstractAbstainingListener * * @var TableManipulator */ - private $tableManipulator; + private TableManipulator $tableManipulator; + + /** + * The MetaModel factory. + * + * @var IFactory + */ + private IFactory $factory; /** * Create a new instance. * * @param RequestScopeDeterminator $scopeDeterminator The scope determinator. * @param TableManipulator $tableManipulator The table manipulator. + * @param IFactory $factory The MetaModel factory. */ - public function __construct(RequestScopeDeterminator $scopeDeterminator, TableManipulator $tableManipulator) - { + public function __construct( + RequestScopeDeterminator $scopeDeterminator, + TableManipulator $tableManipulator, + IFactory $factory + ) { parent::__construct($scopeDeterminator); $this->tableManipulator = $tableManipulator; + $this->factory = $factory; } /** @@ -60,42 +78,61 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator, TableMa * * @throws \RuntimeException On invalid table names. */ - public function handle(EncodePropertyValueFromWidgetEvent $event) + public function handle(EncodePropertyValueFromWidgetEvent $event): void { if (!$this->wantToHandle($event) || ($event->getProperty() !== 'tableName')) { return; } - // See #49. - $tableName = strtolower($event->getValue()); + // See #49 (We can no longer find the correct issue number... :(). + $tableName = \strtolower($event->getValue()); + + $translator = $event->getEnvironment()->getTranslator(); + assert($translator instanceof TranslatorInterface); - if (!strlen($tableName)) { - throw new \RuntimeException('Table name not given'); + if ('' === $tableName) { + throw new \RuntimeException($translator->translate('ERR.tableNameNotGiven', 'tl_metamodel')); } // Force mm_ prefix. - if (substr($tableName, 0, 3) !== 'mm_') { + if (!\str_starts_with($tableName, 'mm_')) { $tableName = 'mm_' . $tableName; } - $dataProvider = $event->getEnvironment()->getDataProvider('tl_metamodel'); - - try { - // New model, ensure the table does not exist. - if (!$event->getModel()->getId()) { - $this->tableManipulator->checkTableDoesNotExist($tableName); - } else { - // Edited model, ensure the value is unique and then that the table does not exist. - $oldVersion = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($event->getModel()->getId())); + // New model, ensure the table does not exist. + if (!$event->getModel()->getId()) { + $this->checkTableName($tableName, $translator); + } else { + $dataProvider = $event->getEnvironment()->getDataProvider('tl_metamodel'); + assert($dataProvider instanceof DataProviderInterface); - if ($oldVersion->getProperty('tableName') !== $event->getModel()->getProperty('tableName')) { - $this->tableManipulator->checkTableDoesNotExist($tableName); - } + // Edited model, ensure the value is unique and then that the table does not exist. + $oldVersion = $dataProvider->fetch($dataProvider->getEmptyConfig()->setId($event->getModel()->getId())); + assert($oldVersion instanceof ModelInterface); + if ($oldVersion->getProperty('tableName') !== $event->getModel()->getProperty('tableName')) { + $this->checkTableName($tableName, $translator); } - } catch (\Exception $exception) { - throw new \RuntimeException($exception->getMessage(), $exception->getCode(), $exception); } $event->setValue($tableName); } + + private function checkTableName(string $tableName, TranslatorInterface $translator): void + { + try { + $this->tableManipulator->checkTablename($tableName); + } catch (InvalidTableNameException $exception) { + throw new \RuntimeException( + $translator->translate('ERR.invalidTableName', 'tl_metamodel', ['%table_name%' => $tableName]), + $exception->getCode(), + $exception + ); + } + $model = $this->factory->getMetaModel($tableName); + if (null !== $model) { + throw new \RuntimeException( + $translator->translate('ERR.tableExists', 'tl_metamodel', ['%table_name%' => $tableName]) + ); + } + } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/TableUpdatingListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/TableUpdatingListener.php deleted file mode 100644 index c8bf4fb6e..000000000 --- a/src/CoreBundle/EventListener/DcGeneral/Table/MetaModel/TableUpdatingListener.php +++ /dev/null @@ -1,104 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\MetaModel; - -use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; -use ContaoCommunityAlliance\DcGeneral\Event\PostPersistModelEvent; -use ContaoCommunityAlliance\DcGeneral\Event\PreDeleteModelEvent; -use MetaModels\Helper\TableManipulator; - -/** - * This handles table renaming and table deleting. - */ -class TableUpdatingListener extends AbstractAbstainingListener -{ - /** - * The table manipulator. - * - * @var TableManipulator - */ - private $tableManipulator; - - /** - * Create a new instance. - * - * @param RequestScopeDeterminator $scopeDeterminator The scope determinator. - * @param TableManipulator $tableManipulator The table manipulator. - */ - public function __construct(RequestScopeDeterminator $scopeDeterminator, TableManipulator $tableManipulator) - { - parent::__construct($scopeDeterminator); - $this->tableManipulator = $tableManipulator; - } - - /** - * Handle the deletion of a MetaModel and all attached data. - * - * @param PreDeleteModelEvent $event The event. - * - * @return void - */ - public function handleDelete(PreDeleteModelEvent $event) - { - if (!$this->wantToHandle($event)) { - return; - } - - try { - $this->tableManipulator->checkTableExists($tableName = $event->getModel()->getProperty('tableName')); - } catch (\Exception $exception) { - // Exit if table does not exist. - return; - } - - $this->tableManipulator->deleteTable($tableName); - } - - /** - * Handle the update of a MetaModel and all attached data. - * - * @param PostPersistModelEvent $event The event. - * - * @return void - */ - public function handleUpdate(PostPersistModelEvent $event) - { - if (!$this->wantToHandle($event)) { - return; - } - - $old = $event->getOriginalModel(); - $new = $event->getModel(); - $oldTable = $old ? $old->getProperty('tableName') : null; - $newTable = $new->getProperty('tableName'); - - // Table name changed? - if ($oldTable !== $newTable) { - if (!empty($oldTable)) { - $this->tableManipulator->renameTable($oldTable, $newTable); - } else { - $this->tableManipulator->createTable($newTable); - } - } - - $this->tableManipulator->setVariantSupport($newTable, $new->getProperty('varsupport')); - } -} diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AbstractAbstainingListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AbstractAbstainingListener.php index f5527adf2..6b7b2b5fc 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AbstractAbstainingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AbstractAbstainingListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -21,6 +22,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\RenderSetting; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; @@ -34,7 +36,7 @@ abstract class AbstractAbstainingListener * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * Create a new instance. @@ -59,13 +61,16 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) return false; } - $environment = $event->getEnvironment(); - if ('tl_metamodel_rendersetting' !== $environment->getDataDefinition()->getName()) { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ('tl_metamodel_rendersetting' !== $dataDefinition->getName()) { return false; } if ($event instanceof AbstractModelAwareEvent) { - if ($event->getEnvironment()->getDataDefinition()->getName() !== $event->getModel()->getProviderName()) { + if ($dataDefinition->getName() !== $event->getModel()->getProviderName()) { return false; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AbstractListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AbstractListener.php index a9531577f..4bba34eab 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AbstractListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AbstractListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +25,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\IFactory; use MetaModels\IMetaModel; @@ -69,15 +70,16 @@ public function __construct( * * @param ModelInterface $model The input screen model for which to retrieve the MetaModel. * - * @return IMetaModel + * @return IMetaModel|NULL * * @throws DcGeneralInvalidArgumentException When an invalid model has been passed or the model does not have an id. + * @throws Exception */ protected function getMetaModelFromModel(ModelInterface $model) { - if (!(($model->getProviderName() == 'tl_metamodel_rendersetting') && $model->getProperty('pid'))) { + if (!(($model->getProviderName() === 'tl_metamodel_rendersetting') && $model->getProperty('pid'))) { throw new DcGeneralInvalidArgumentException( - sprintf( + \sprintf( 'Model must originate from tl_metamodel_rendersetting and be saved, this one originates from ' . '%s and has pid %s', $model->getProviderName(), @@ -92,8 +94,12 @@ protected function getMetaModelFromModel(ModelInterface $model) ->from('tl_metamodel_rendersettings', 't') ->where('t.id=:id') ->setParameter('id', $model->getProperty('pid')) - ->execute() - ->fetchColumn(); + ->executeQuery() + ->fetchOne(); + + if (false === $metaModelId) { + return null; + } $tableName = $this->factory->translateIdToMetaModelName($metaModelId); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AddAllButtonListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AddAllButtonListener.php index 9f7ff3e52..0d17b4ecb 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AddAllButtonListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AddAllButtonListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -22,6 +23,8 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetGlobalButtonEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelId; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\DcGeneral\InputProviderInterface; use Doctrine\DBAL\Connection; use MetaModels\IFactory; use Symfony\Component\Routing\Generator\UrlGeneratorInterface; @@ -36,21 +39,21 @@ class AddAllButtonListener * * @var Connection */ - private $connection; + private Connection $connection; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The URL generator. * * @var UrlGeneratorInterface */ - private $urlGenerator; + private UrlGeneratorInterface $urlGenerator; /** * Create a new instance. @@ -72,24 +75,38 @@ public function __construct(Connection $connection, IFactory $factory, UrlGenera * @param GetGlobalButtonEvent $event The event. * * @return void + * + * @throws \Doctrine\DBAL\Exception */ public function getGlobalButton(GetGlobalButtonEvent $event) { - $environment = $event->getEnvironment(); - if ('addall' !== $event->getKey() - || 'tl_metamodel_rendersetting' !== $environment->getDataDefinition()->getName()) { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + 'addall' !== $event->getKey() + || 'tl_metamodel_rendersetting' !== $dataDefinition->getName() + ) { return; } - $renderSetting = ModelId::fromSerialized($environment->getInputProvider()->getParameter('pid'))->getId(); + $inputProvider = $environment->getInputProvider(); + assert($inputProvider instanceof InputProviderInterface); + + $renderSetting = ModelId::fromSerialized($inputProvider->getParameter('pid'))->getId(); $modelId = $this->connection->createQueryBuilder() ->select('r.pid') ->from('tl_metamodel_rendersettings', 'r') ->where('r.id=:pid') ->setParameter('pid', $renderSetting) - ->execute() - ->fetchColumn(); + ->executeQuery() + ->fetchOne(); + + if (false === $modelId) { + return; + } $name = $this->factory->translateIdToMetaModelName($modelId); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AttributeOptionsListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AttributeOptionsListener.php index 441fdd843..7361050a1 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AttributeOptionsListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/AttributeOptionsListener.php @@ -38,21 +38,21 @@ class AttributeOptionsListener extends AbstractListener * * @var SelectAttributeOptionLabelFormatter */ - private $attributeLabelFormatter; + private SelectAttributeOptionLabelFormatter $labelFormatter; /** * {@inheritDoc} * - * @param SelectAttributeOptionLabelFormatter $attributeLabelFormatter The attribute select option label formatter. + * @param SelectAttributeOptionLabelFormatter $labelFormatter The attribute select option label formatter. */ public function __construct( RequestScopeDeterminator $scopeDeterminator, IFactory $factory, Connection $connection, - SelectAttributeOptionLabelFormatter $attributeLabelFormatter + SelectAttributeOptionLabelFormatter $labelFormatter ) { parent::__construct($scopeDeterminator, $factory, $connection); - $this->attributeLabelFormatter = $attributeLabelFormatter; + $this->labelFormatter = $labelFormatter; } /** @@ -71,7 +71,7 @@ public function handle(GetPropertyOptionsEvent $event) $model = $event->getModel(); $metaModel = $this->getMetaModelFromModel($model); - if (!$metaModel) { + if (null === $metaModel) { return; } @@ -88,16 +88,17 @@ public function handle(GetPropertyOptionsEvent $event) ->andWhere('t.attr_id<>:id') ->setParameter('id', $attributeId); } - $alreadyTaken = $alreadyTaken->execute()->fetchAll(\PDO::FETCH_COLUMN); + $alreadyTaken = $alreadyTaken->executeQuery()->fetchFirstColumn(); $options = []; foreach ($metaModel->getAttributes() as $attribute) { - if ($attribute instanceof IInternal + if ( + $attribute instanceof IInternal || in_array($attribute->get('id'), $alreadyTaken) ) { continue; } - $options[$attribute->get('id')] = $this->attributeLabelFormatter->formatLabel($attribute); + $options[$attribute->get('id')] = $this->labelFormatter->formatLabel($attribute); } $event->setOptions($options); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/ModelToLabelListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/ModelToLabelListener.php index de661750a..850215805 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/ModelToLabelListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/ModelToLabelListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,7 +27,8 @@ use MetaModels\Attribute\IAttributeFactory; use MetaModels\CoreBundle\Assets\IconBuilder; use MetaModels\IFactory; -use Symfony\Component\Translation\TranslatorInterface; +use MetaModels\IMetaModel; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This handles the rendering of models to labels. @@ -39,21 +40,21 @@ class ModelToLabelListener extends AbstractListener * * @var IAttributeFactory */ - private $attributeFactory; + private IAttributeFactory $attributeFactory; /** * The icon builder. * * @var IconBuilder */ - private $iconBuilder; + private IconBuilder $iconBuilder; /** * The translator. * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * Create a new instance. @@ -94,7 +95,8 @@ public function handle(ModelToLabelEvent $event) $model = $event->getModel(); $metaModel = $this->getMetaModelFromModel($model); - $attribute = $metaModel->getAttributeById($model->getProperty('attr_id')); + assert($metaModel instanceof IMetaModel); + $attribute = $metaModel->getAttributeById((int) $model->getProperty('attr_id')); if ($attribute) { $type = $attribute->get('type'); @@ -108,13 +110,14 @@ public function handle(ModelToLabelEvent $event) $name = $attribute->getName(); $colName = $attribute->getColName(); } else { - $type = $this->trans('error_unknown_id', [$model->getProperty('attr_id')]); + $type = $this->trans('error_unknown_id', ['%id%' => $model->getProperty('attr_id')]); $image = $this->iconBuilder->getBackendIconImageTag('bundles/metamodelscore/images/icons/fields.png'); $variant = ''; $name = $this->trans('error_unknown_attribute'); $colName = $this->trans('error_unknown_column'); } + /** @psalm-suppress InvalidArgument */ $event ->setLabel('
%s [%s%s]
@@ -134,17 +137,12 @@ public function handle(ModelToLabelEvent $event) * Translate a key. * * @param string $key The key to translate. - * * @param array $params The parameters. * * @return string */ - private function trans($key, $params = []) + private function trans(string $key, array $params = []): string { - return $this->translator->trans( - 'tl_metamodel_rendersettings.' . $key, - $params, - 'contao_tl_metamodel_rendersettings' - ); + return $this->translator->trans($key, $params, 'tl_metamodel_rendersettings'); } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/PaletteRestrictionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/PaletteRestrictionListener.php index 8031aee09..67892d9ed 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/PaletteRestrictionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/PaletteRestrictionListener.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. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author David Molineus - * @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 */ @@ -40,7 +41,7 @@ class PaletteRestrictionListener extends AbstractPaletteRestrictionListener * * @var Connection */ - private $connection; + private Connection $connection; /** * PaletteRestrictionListener constructor. @@ -73,11 +74,12 @@ public function handle(BuildDataDefinitionEvent $event) foreach ($palettes->getPalettes() as $palette) { if ($palette->getName() !== 'default') { $paletteCondition = $palette->getCondition(); - if (!($paletteCondition instanceof ConditionChainInterface) + if ( + !($paletteCondition instanceof ConditionChainInterface) || ($paletteCondition->getConjunction() !== PaletteConditionChain::OR_CONJUNCTION) ) { $paletteCondition = new PaletteConditionChain( - $paletteCondition ? array($paletteCondition) : array(), + null !== $paletteCondition ? [$paletteCondition] : [], PaletteConditionChain::OR_CONJUNCTION ); $palette->setCondition($paletteCondition); @@ -107,7 +109,7 @@ private function buildMetaPaletteConditions($palette, $metaPalettes) continue; } - if (preg_match('#^(\w+) extends (\w+)$#', $typeName, $matches)) { + if (\preg_match('#^(\w+) extends (\w+)$#', $typeName, $matches)) { $typeName = $matches[1]; } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/TemplateOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/TemplateOptionListener.php index c2101005f..92d770919 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/TemplateOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSetting/TemplateOptionListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,7 @@ use Doctrine\DBAL\Connection; use MetaModels\BackendIntegration\TemplateList; use MetaModels\IFactory; +use MetaModels\IMetaModel; /** * This handles the providing of available templates. @@ -71,7 +73,8 @@ public function handle(GetPropertyOptionsEvent $event) $model = $event->getModel(); $metaModel = $this->getMetaModelFromModel($model); - $attribute = $metaModel->getAttributeById($model->getProperty('attr_id')); + assert($metaModel instanceof IMetaModel); + $attribute = $metaModel->getAttributeById((int) $model->getProperty('attr_id')); if (!$attribute) { return; diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/AbstractAbstainingListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/AbstractAbstainingListener.php index b670b021a..3784e39f1 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/AbstractAbstainingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/AbstractAbstainingListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -21,6 +22,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\RenderSettings; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; @@ -34,7 +36,7 @@ abstract class AbstractAbstainingListener * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * Create a new instance. @@ -59,13 +61,16 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) return false; } - $environment = $event->getEnvironment(); - if ('tl_metamodel_rendersettings' !== $environment->getDataDefinition()->getName()) { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ('tl_metamodel_rendersettings' !== $dataDefinition->getName()) { return false; } if ($event instanceof AbstractModelAwareEvent) { - if ($event->getEnvironment()->getDataDefinition()->getName() !== $event->getModel()->getProviderName()) { + if ($dataDefinition->getName() !== $event->getModel()->getProviderName()) { return false; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/AddAssetListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/AddAssetListener.php index d0830fe55..e7f9e8382 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/AddAssetListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/AddAssetListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 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. @@ -15,7 +15,8 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Andreas Fischer - * @copyright 2012-2021 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 */ @@ -23,9 +24,10 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\RenderSettings; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use MenAtWork\MultiColumnWizardBundle\Event\GetOptionsEvent; +use Symfony\Component\Filesystem\Path; use Symfony\Component\Finder\Finder; -use Webmozart\PathUtil\Path; /** * This handles the rendering of models to labels. @@ -37,14 +39,14 @@ class AddAssetListener * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * The upload path to scan within. * * @var string */ - private $uploadPath; + private string $uploadPath; /** * Create a new instance. @@ -67,9 +69,11 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator, $upload */ public function getStylesheets(GetOptionsEvent $event) { - if (!$this->wantToHandle($event) + if ( + !$this->wantToHandle($event) || ($event->getPropertyName() !== 'additionalCss') - || ($event->getSubPropertyName() !== 'file')) { + || ($event->getSubPropertyName() !== 'file') + ) { return; } @@ -85,9 +89,14 @@ public function getStylesheets(GetOptionsEvent $event) */ public function getJavascripts(GetOptionsEvent $event) { - if (($event->getEnvironment()->getDataDefinition()->getName() !== 'tl_metamodel_rendersettings') + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ( + ($dataDefinition->getName() !== 'tl_metamodel_rendersettings') || ($event->getPropertyName() !== 'additionalJs') - || ($event->getSubPropertyName() !== 'file')) { + || ($event->getSubPropertyName() !== 'file') + ) { return; } @@ -104,7 +113,8 @@ public function getJavascripts(GetOptionsEvent $event) private function scanFiles($extension) { $files = []; - foreach (Finder::create() + foreach ( + Finder::create() ->followLinks() ->in($this->uploadPath) ->name('*.' . $extension) @@ -129,12 +139,15 @@ private function wantToHandle(GetOptionsEvent $event) return false; } - $environment = $event->getEnvironment(); - if ('tl_metamodel_rendersettings' !== $environment->getDataDefinition()->getName()) { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + if ('tl_metamodel_rendersettings' !== $dataDefinition->getName()) { return false; } - if ($event->getEnvironment()->getDataDefinition()->getName() !== $event->getModel()->getProviderName()) { + if ($dataDefinition->getName() !== $event->getModel()->getProviderName()) { return false; } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/JumpToListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/JumpToListener.php index b5681f852..0efaac7be 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/JumpToListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/JumpToListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,9 +27,11 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\DecodePropertyValueForWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\EncodePropertyValueFromWidgetEvent; use ContaoCommunityAlliance\DcGeneral\Data\ModelInterface; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use Doctrine\DBAL\Connection; use MetaModels\IFactory; -use Symfony\Component\Translation\TranslatorInterface; +use MetaModels\IMetaModel; +use Symfony\Contracts\Translation\TranslatorInterface; /** * This handles the rendering of models to labels. @@ -41,21 +43,21 @@ class JumpToListener extends AbstractAbstainingListener * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * The translator. * * @var TranslatorInterface */ - private $translator; + private TranslatorInterface $translator; /** * Create a new instance. @@ -90,23 +92,27 @@ public function decodeValue(DecodePropertyValueForWidgetEvent $event) return; } - $propInfo = $event->getEnvironment()->getDataDefinition()->getPropertiesDefinition()->getProperty('jumpTo'); + $dataDefinition = $event->getEnvironment()->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + + $propInfo = $dataDefinition->getPropertiesDefinition()->getProperty('jumpTo'); $value = StringUtil::deserialize($event->getValue(), true); $extra = $propInfo->getExtra(); $newValues = []; - $languages = $extra['columnFields']['langcode']['options']; - foreach (array_keys($languages) as $key) { + /** @var array $languages */ + $languages = $extra['columnFields']['langcode']['options'] ?? []; + foreach (\array_keys($languages) as $key) { $newValue = ''; $filter = 0; if ($value) { foreach ($value as $arr) { - if (!is_array($arr)) { + if (!\is_array($arr)) { break; } // Set the new value and exit the loop. - if (array_search($key, $arr) !== false) { + if (\in_array($key, $arr, true)) { $newValue = '{{link_url::' . $arr['value'] . '}}'; $filter = $arr['filter']; break; @@ -141,10 +147,10 @@ public function encodeValue(EncodePropertyValueFromWidgetEvent $event) $value = StringUtil::deserialize($event->getValue(), true); foreach ($value as $k => $v) { - $value[$k]['value'] = str_replace(['{{link_url::', '}}'], ['', ''], $v['value']); + $value[$k]['value'] = \str_replace(['{{link_url::', '}}'], ['', ''], $v['value']); } - $event->setValue(serialize($value)); + $event->setValue(\serialize($value)); } /** @@ -166,21 +172,29 @@ public function buildWidget(BuildWidgetEvent $event) $model = $event->getModel(); $metaModel = $this->factory->getMetaModel($this->factory->translateIdToMetaModelName($model->getProperty('pid'))); + assert($metaModel instanceof IMetaModel); $extra = $event->getProperty()->getExtra(); + /** @psalm-suppress DeprecatedMethod */ if ($metaModel->isTranslated()) { + /** @psalm-suppress DeprecatedMethod */ + $fallback = $metaModel->getFallbackLanguage(); + $arrLanguages = []; + $rowClasses = []; + /** @psalm-suppress DeprecatedMethod */ foreach ((array) $metaModel->getAvailableLanguages() as $strLangCode) { $arrLanguages[$strLangCode] = $this->translator - ->trans('LNG.'. $strLangCode, [], 'contao_languages'); + ->trans('LNG.' . $strLangCode, [], 'contao_languages'); + $rowClasses[] = ($strLangCode === $fallback) ? 'fallback_language' : 'normal_language'; } - asort($arrLanguages); - $extra['minCount'] = count($arrLanguages); - $extra['maxCount'] = count($arrLanguages); + $extra['minCount'] = \count($arrLanguages); + $extra['maxCount'] = \count($arrLanguages); - $extra['columnFields']['langcode']['options'] = $arrLanguages; + $extra['columnFields']['langcode']['options'] = $arrLanguages; + $extra['columnFields']['langcode']['eval']['rowClasses'] = $rowClasses; } else { $extra['minCount'] = 1; $extra['maxCount'] = 1; @@ -188,9 +202,9 @@ public function buildWidget(BuildWidgetEvent $event) $extra['columnFields']['langcode']['options'] = [ 'xx' => $this->translator ->trans( - 'tl_metamodel_rendersettings.jumpTo_allLanguages', + 'jumpTo_allLanguages', [], - 'contao_tl_metamodel_rendersettings' + 'tl_metamodel_rendersettings' ) ]; } @@ -216,8 +230,8 @@ private function getFilterSettings(ModelInterface $model) ->from('tl_metamodel_filter', 't') ->where('t.pid=:id') ->setParameter('id', $model->getProperty('pid')) - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); $result = []; foreach ($filters as $filter) { diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/LanguageCodeWizardListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/LanguageCodeWizardListener.php index c31a4e06d..c9db4b37b 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/LanguageCodeWizardListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/RenderSettings/LanguageCodeWizardListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Maack * @author Stefan Heimes * @author Sven Baumann - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,23 +27,30 @@ use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Image\GenerateHtmlEvent; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; -use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminatorAwareTrait; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\ContaoBackendViewTemplate; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ManipulateWidgetEvent; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; +use ContaoCommunityAlliance\Translator\TranslatorInterface; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** - * This handles the wizard button for the language code jump to field in the multicolumn wizard. + * This handles the wizard button for the language code jump to field in the multi-column wizard. */ class LanguageCodeWizardListener { - use RequestScopeDeterminatorAwareTrait; + /** + * The request mode determinator. + * + * @var RequestScopeDeterminator + */ + private RequestScopeDeterminator $scopeDeterminator; /** * The picker builder. * * @var PickerBuilderInterface */ - private $pickerBuilder; + private PickerBuilderInterface $pickerBuilder; /** * LanguageCodeWizardListener constructor. @@ -66,32 +73,40 @@ public function __construct(RequestScopeDeterminator $scopeDeterminator, PickerB */ public function pagePicker(ManipulateWidgetEvent $event) { - if (!$this->scopeDeterminator->currentScopeIsBackend() - || !('tl_metamodel_rendersettings' === $event->getEnvironment()->getDataDefinition()->getName()) - || !((0 === strpos($event->getProperty()->getName(), 'jumpTo')) - && ('[value]' === substr($event->getProperty()->getName(), -\strlen('[value]')))) + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ( + !$this->scopeDeterminator->currentScopeIsBackend() + || !('tl_metamodel_rendersettings' === $dataDefinition->getName()) + || !((\str_starts_with($event->getProperty()->getName(), 'jumpTo')) + && (\str_ends_with($event->getProperty()->getName(), '[value]'))) ) { return; } - $environment = $event->getEnvironment(); - $pickerUrl = $this->pickerBuilder->getUrl('cca_link'); + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + $urlEvent = new GenerateHtmlEvent( 'pickpage.svg', - $environment->getTranslator()->translate('MSC.pagepicker'), + $translator->translate('pagePicker', 'dc-general'), 'style="vertical-middle:top;cursor:pointer"' ); - $environment->getEventDispatcher()->dispatch($urlEvent, ContaoEvents::IMAGE_GET_HTML); + $dispatcher = $environment->getEventDispatcher(); + assert($dispatcher instanceof EventDispatcherInterface); + + $dispatcher->dispatch($urlEvent, ContaoEvents::IMAGE_GET_HTML); $template = new ContaoBackendViewTemplate('dc_general_wizard_link_url_picker'); $template ->set('name', $event->getWidget()->name) ->set('popupUrl', $pickerUrl) - ->set('html', ' ' . $urlEvent->getHtml()) - ->set('label', $event->getProperty()->getLabel()[1]) + ->set('html', ' ' . (string) $urlEvent->getHtml()) + ->set('label', $translator->translate($event->getProperty()->getLabel(), $dataDefinition->getName())) ->set('id', $event->getWidget()->id); $event->getWidget()->wizard = $template->parse(); diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/AbstractAbstainingListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/AbstractAbstainingListener.php index 7e2b3e2e6..1911f70b2 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/AbstractAbstainingListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/AbstractAbstainingListener.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. @@ -13,7 +13,7 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,6 +21,7 @@ namespace MetaModels\CoreBundle\EventListener\DcGeneral\Table\SearchablePages; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractEnvironmentAwareEvent; use ContaoCommunityAlliance\DcGeneral\Event\AbstractModelAwareEvent; @@ -34,7 +35,7 @@ abstract class AbstractAbstainingListener * * @var RequestScopeDeterminator */ - private $scopeDeterminator; + private RequestScopeDeterminator $scopeDeterminator; /** * Create a new instance. @@ -59,13 +60,15 @@ protected function wantToHandle(AbstractEnvironmentAwareEvent $event) return false; } - $environment = $event->getEnvironment(); - if ('tl_metamodel_searchable_pages' !== $environment->getDataDefinition()->getName()) { + $environment = $event->getEnvironment(); + $dataDefinition = $environment->getDataDefinition(); + assert($dataDefinition instanceof ContainerInterface); + if ('tl_metamodel_searchable_pages' !== $dataDefinition->getName()) { return false; } if ($event instanceof AbstractModelAwareEvent) { - if ($event->getEnvironment()->getDataDefinition()->getName() !== $event->getModel()->getProviderName()) { + if ($dataDefinition->getName() !== $event->getModel()->getProviderName()) { return false; } } diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/FilterOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/FilterOptionListener.php index 12d5c3ed1..3c20a9d78 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/FilterOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/FilterOptionListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,7 +35,7 @@ class FilterOptionListener extends AbstractAbstainingListener * * @var Connection */ - private $connection; + private Connection $connection; /** * Create a new instance. @@ -72,8 +72,8 @@ public function handle(GetPropertyOptionsEvent $event) ->from('tl_metamodel_filter', 't') ->where('t.pid=:id') ->setParameter('id', $model->getProperty('pid')) - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); $options = []; foreach ($filters as $filter) { diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/PaletteRestrictionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/PaletteRestrictionListener.php index 687c8473c..716d8551e 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/PaletteRestrictionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/PaletteRestrictionListener.php @@ -50,10 +50,11 @@ public function handle(BuildDataDefinitionEvent $event) } $chain = $property->getVisibleCondition(); - if (!($chain + if ( + !($chain && ($chain instanceof PropertyConditionChain) && $chain->getConjunction() == PropertyConditionChain::AND_CONJUNCTION - ) + ) ) { $chain = new PropertyConditionChain( $chain ?: array(), diff --git a/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/RenderSettingOptionListener.php b/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/RenderSettingOptionListener.php index 64e896c08..c7b30aca4 100644 --- a/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/RenderSettingOptionListener.php +++ b/src/CoreBundle/EventListener/DcGeneral/Table/SearchablePages/RenderSettingOptionListener.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -34,7 +35,7 @@ class RenderSettingOptionListener extends AbstractAbstainingListener * * @var Connection */ - private $connection; + private Connection $connection; /** * Create a new instance. @@ -67,12 +68,12 @@ public function handle(GetPropertyOptionsEvent $event) } $filters = $this->connection ->createQueryBuilder() - ->select('id', 'name') - ->from('tl_metamodel_rendersettings') - ->where('pid=:id') + ->select('t.id', 't.name') + ->from('tl_metamodel_rendersettings', 't') + ->where('t.pid=:id') ->setParameter('id', $model->getProperty('pid')) - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); $options = []; foreach ($filters as $filter) { diff --git a/src/CoreBundle/EventListener/DoctrineSchemaListener.php b/src/CoreBundle/EventListener/DoctrineSchemaListener.php new file mode 100644 index 000000000..a9ae20f7f --- /dev/null +++ b/src/CoreBundle/EventListener/DoctrineSchemaListener.php @@ -0,0 +1,183 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\EventListener; + +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\Type; +use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs; +use LogicException; +use MetaModels\InformationProvider\MetaModelInformationCollector; +use MetaModels\Schema\Doctrine\DoctrineSchemaInformation; +use MetaModels\Schema\SchemaGenerator; +use MetaModels\Schema\SchemaInformation; + +final class DoctrineSchemaListener +{ + private SchemaGenerator $generator; + + private MetaModelInformationCollector $collector; + + public function __construct( + SchemaGenerator $generator, + MetaModelInformationCollector $collector + ) { + $this->generator = $generator; + $this->collector = $collector; + } + + /** + * Adds the MetaModels database schema information to the Doctrine schema. + */ + public function postGenerateSchema(GenerateSchemaEventArgs $event): void + { + $this->generator->generate($information = new SchemaInformation(), $this->collector->getCollection()); + + $contaoSchema = $event->getSchema(); + $schema = $information->get(DoctrineSchemaInformation::class); + if (!$schema instanceof DoctrineSchemaInformation) { + throw new LogicException('Invalid schema information obtained.'); + } + $this->mergeSchema($schema, $contaoSchema); + } + + private function mergeSchema(DoctrineSchemaInformation $source, Schema $target): void + { + foreach ($source->getSchema()->getTables() as $sourceTable) { + $tableName = $sourceTable->getName(); + if (!$target->hasTable($tableName)) { + $target->createTable($tableName); + } + $targetTable = $target->getTable($tableName); + $this->mergeTable($sourceTable, $targetTable); + } + } + + private function mergeTable(Table $sourceTable, Table $targetTable): void + { + $this->mergeColumns($sourceTable, $targetTable); + $this->mergeIndexes($sourceTable, $targetTable); + $this->mergeUniqueConstraints($sourceTable, $targetTable); + $this->mergeForeignKeyConstraints($sourceTable, $targetTable); + foreach ($sourceTable->getOptions() as $optionName => $optionValue) { + $targetTable->addOption($optionName, $optionValue); + } + + // NOTE: We are explicitely not copying the property: SchemaConfig|null Table::$_schemaConfig + } + + private function mergeColumns(Table $sourceTable, Table $targetTable): void + { + $registry = Type::getTypeRegistry(); + foreach ($sourceTable->getColumns() as $sourceColumn) { + $name = $sourceColumn->getName(); + $options = [ + 'default' => $sourceColumn->getDefault(), + 'notnull' => $sourceColumn->getNotnull(), + 'length' => $sourceColumn->getLength(), + 'precision' => $sourceColumn->getPrecision(), + 'scale' => $sourceColumn->getScale(), + 'fixed' => $sourceColumn->getFixed(), + 'unsigned' => $sourceColumn->getUnsigned(), + 'autoincrement' => $sourceColumn->getAutoincrement(), + 'columnDefinition' => $sourceColumn->getColumnDefinition(), + 'comment' => $sourceColumn->getComment(), + ]; + + $platformOptions = $sourceColumn->getPlatformOptions(); + if ($targetTable->hasColumn($name)) { + $platformOptions = \array_merge($targetTable->getColumn($name)->getPlatformOptions(), $platformOptions); + } + + $targetTable + ->addColumn($name, $registry->lookupName($sourceColumn->getType()), $options) + ->setPlatformOptions($platformOptions); + } + } + + private function mergeIndexes(Table $sourceTable, Table $targetTable): void + { + foreach ($sourceTable->getIndexes() as $source) { + $name = $source->getName(); + $columns = $source->getColumns(); + $flags = $source->getFlags(); + $options = $source->getOptions(); + $unique = $source->isUnique(); + $primary = $source->isPrimary(); + if ($targetTable->hasIndex($name)) { + $tmpIndex = $targetTable->getIndex($name); + $targetTable->dropIndex($name); + $columns = \array_merge($tmpIndex->getColumns(), $columns); + $flags = \array_merge($tmpIndex->getFlags(), $flags); + $options = \array_merge($tmpIndex->getOptions(), $options); + $unique = $unique || $source->isUnique(); + $primary = $primary || $source->isPrimary(); + } + if ($primary) { + $targetTable->setPrimaryKey($columns, $name); + continue; + } + if ($unique) { + $targetTable->addUniqueIndex($columns, $name, $options); + continue; + } + $targetTable->addIndex($columns, $name, $flags, $options); + } + } + + private function mergeUniqueConstraints(Table $sourceTable, Table $targetTable): void + { + foreach ($sourceTable->getUniqueConstraints() as $uniqueConstraint) { + $name = $uniqueConstraint->getName(); + $columns = $uniqueConstraint->getColumns(); + $flags = $uniqueConstraint->getFlags(); + $options = $uniqueConstraint->getOptions(); + if ($targetTable->hasUniqueConstraint($name)) { + $tmpUniqueConstraint = $targetTable->getUniqueConstraint($name); + $targetTable->removeUniqueConstraint($name); + $columns = \array_merge($tmpUniqueConstraint->getColumns(), $columns); + $flags = \array_merge($tmpUniqueConstraint->getFlags(), $flags); + $options = \array_merge($tmpUniqueConstraint->getOptions(), $options); + } + + $targetTable->addUniqueConstraint($columns, $name, $flags, $options); + } + } + + private function mergeForeignKeyConstraints(Table $sourceTable, Table $targetTable): void + { + foreach ($sourceTable->getForeignKeys() as $foreignKey) { + $name = $foreignKey->getName(); + $foreignTable = $foreignKey->getForeignTableName(); + $localColumns = $foreignKey->getLocalColumns(); + $foreignColumns = $foreignKey->getForeignColumns(); + $options = $foreignKey->getOptions(); + if ($targetTable->hasForeignKey($name)) { + $tmpUniqueConstraint = $targetTable->getForeignKey($name); + $targetTable->removeUniqueConstraint($name); + $localColumns = \array_merge($tmpUniqueConstraint->getLocalColumns(), $localColumns); + $foreignColumns = \array_merge($tmpUniqueConstraint->getForeignColumns(), $foreignColumns); + $options = \array_merge($tmpUniqueConstraint->getOptions(), $options); + } + $targetTable->addForeignKeyConstraint($foreignTable, $localColumns, $foreignColumns, $options, $name); + } + } +} diff --git a/src/CoreBundle/EventListener/GetSearchablePagesListener.php b/src/CoreBundle/EventListener/GetSearchablePagesListener.php index 16ad9c44a..86cbe20cb 100644 --- a/src/CoreBundle/EventListener/GetSearchablePagesListener.php +++ b/src/CoreBundle/EventListener/GetSearchablePagesListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2023 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,86 +17,96 @@ * @author Ingolf Steinhardt * @author David Molineus * @author Richard Henkenjohann - * @copyright 2012-2023 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener; -use Contao\CoreBundle\ServiceAnnotation\Hook; -use Contao\StringUtil; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\GenerateFrontendUrlEvent; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\GetPageDetailsEvent; use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; +use Contao\CoreBundle\Event\SitemapEvent; +use Contao\Environment; +use Contao\StringUtil; +use DOMElement; +use DOMException; +use DOMNode; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; +use Doctrine\DBAL\Exception; +use Generator; use MetaModels\Filter\IFilter; -use MetaModels\Filter\Setting\ICollection as IFilterSettingCollection; use MetaModels\Filter\Setting\IFilterSettingFactory; use MetaModels\IFactory; +use MetaModels\IItems; use MetaModels\IMetaModel; use MetaModels\Item; use MetaModels\ITranslatedMetaModel; use MetaModels\Render\Setting\ICollection as IRenderSettingCollection; use MetaModels\Render\Setting\IRenderSettingFactory; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Terminal42\ServiceAnnotationBundle\ServiceAnnotationInterface; +use RuntimeException; +use Symfony\Contracts\EventDispatcher\EventDispatcherInterface; + +use function array_merge; +use function array_values; +use function in_array; +use function trim; /** * Class SearchablePages. + * + * @psalm-type TSearchablePageConfig=array{ + * id: int, + * pid: int, + * tstamp: int, + * name: string, + * filter: int, + * filterparams: string, + * rendersetting: int, + * published: string + * } + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ -class GetSearchablePagesListener implements ServiceAnnotationInterface +class GetSearchablePagesListener { - /** - * A list with all pages found by Contao. - * - * @var array - */ - protected $foundPages = []; - - /** - * A list with all settings from the database. - * - * @var array - */ - private $configs = []; - /** * Database connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * Factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Event dispatcher. * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * Filter setting factory. * * @var IFilterSettingFactory */ - private $filterSettingFactory; + private IFilterSettingFactory $filterSettingFactory; /** * Render setting factory. * * @var IRenderSettingFactory */ - private $renderSettingFactory; + private IRenderSettingFactory $renderSettingFactory; /** * Construct. @@ -122,458 +132,286 @@ public function __construct( } /** - * Start point for the hook getSearchablePages. - * - * @param array $pages List with all pages. - * @param int|null $rootPage ID of the root page. - * @param bool|null $fromSiteMap True when called from sitemap generator, null otherwise. - * @param string|null $language The current language. - * - * @return array + * Start point for the contao sitemap event. * - * @throws \Doctrine\DBAL\DBALException When an database error occur. - * - * @see \RebuildIndex::run() - * @see \Automator::generateSitemap() - * - * @Hook("getSearchablePages") - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @throws DOMException|Exception */ - public function __invoke($pages, $rootPage = null, $fromSiteMap = false, $language = null) + public function __invoke(SitemapEvent $event): void { - // Save the pages. - $this->foundPages = $pages; - // Run each entry in the published config array. - foreach ($this->getConfigs() as $config) { - if (!$config['published']) { - continue; - } - $this->getMetaModelsPages( - $config, - $rootPage, - $language - ); - } + $rootPageIds = $event->getRootPageIds(); + $sitemap = $event->getDocument(); + $urlSet = $sitemap->childNodes[0]; + assert($urlSet instanceof DOMElement); + + // Run each entry in the published config array and search detail pages. + foreach ($this->getPublishedConfigs() as $config) { + $metaModelId = (string) $config['pid']; + $metaModel = $this->getMetaModel($metaModelId); + assert($metaModel instanceof IMetaModel); + $filterParams = StringUtil::deserialize($config['filterparams'], true); + $listFilter = $this->getListFilter($metaModel, (string) $config['filter'], $filterParams); + $renderSetting = + $this->renderSettingFactory->createCollection($metaModel, (string) $config['rendersetting']); + + // Now loop over all detail pages... + foreach ((array) $renderSetting->get('jumpTo') as $jumpTo) { + if (empty($jumpTo['langcode']) || empty($jumpTo['value']) || empty($jumpTo['filter'])) { + continue; + } - asort($this->foundPages); + $event = new GetPageDetailsEvent((int) $jumpTo['value']); + $this->dispatcher->dispatch($event, ContaoEvents::CONTROLLER_GET_PAGE_DETAILS); + $pageDetails = $event->getPageDetails(); - return $this->foundPages; - } + // If details page not found or root page is not within passed list, continue. + if (empty($pageDetails) || !in_array($pageDetails['rootId'], $rootPageIds, true)) { + continue; + } - /** - * Get all configs. - * - * @return array - * - * @throws \Doctrine\DBAL\DBALException When a database error occur. - */ - protected function getConfigs(): array - { - if (!count($this->configs)) { - // Init the config from database. - $statement = $this->connection - ->createQueryBuilder() - ->select('t.*') - ->from('tl_metamodel_searchable_pages', 't') - ->execute(); - - $this->configs = $statement->fetchAll(FetchMode::ASSOCIATIVE); + $this->removePlainDetailsUrl($pageDetails, $urlSet); + + $langCode = $jumpTo['langcode']; + $filterSetting = $this->filterSettingFactory->createCollection($jumpTo['filter']); + $filterAttributes = $filterSetting->getReferencedAttributes(); + + foreach ( + $this->generateUrlsFor( + $metaModel, + $langCode, + $renderSetting, + $pageDetails, + $filterAttributes, + $listFilter + ) as $url + ) { + $loc = $sitemap->createElement('loc', $url); + $urlEl = $sitemap->createElement('url'); + $urlEl->appendChild($loc); + $urlSet->appendChild($urlEl); + } + } } - - return $this->configs; } /** - * Get a MetaModels by name or id. + * Generate URLs for detail page. * - * @param string|int $identifier The Name or ID of a MetaModels. + * @param IMetaModel $metaModel The MetaModel. + * @param string $language The language. + * @param IRenderSettingCollection $renderSetting The render settings for the detail page. + * @param array $pageDetails The page information for the detail page. + * @param array $filterAttributes The filter attributes. + * @param IFilter $listFilter The list filter. * - * @param boolean $ignoreError If true ignore errors like the MetaModels was not found. - * - * @return IMetaModel|null + * @return Generator * - * @throws \RuntimeException When the MetaModels is missing. + * @SuppressWarnings(PHPMD.Superglobals) */ - protected function getMetaModel($identifier, $ignoreError): ?IMetaModel - { - // Id to name. - if (is_numeric($identifier)) { - $identifier = $this->factory->translateIdToMetaModelName($identifier); - } - - // Create mm, if yowl is true check if we have really a mm . - $metaModels = $this->factory->getMetaModel($identifier); + private function generateUrlsFor( + IMetaModel $metaModel, + string $language, + IRenderSettingCollection $renderSetting, + array $pageDetails, + array $filterAttributes, + IFilter $listFilter + ): Generator { + // Save language. + $currentLanguage = $GLOBALS['TL_LANGUAGE']; + + // Try to generate URLs. + try { + if ($metaModel instanceof ITranslatedMetaModel) { + $prevLanguage = $metaModel->selectLanguage($language); + } + $GLOBALS['TL_LANGUAGE'] = $language; + + $items = $metaModel->findByFilter( + $listFilter, + '', + 0, + 0, + 'ASC', + array_values(array_merge($renderSetting->getSettingNames(), $filterAttributes)) + ); - // If $ignoreError is off and we have no mm throw a new exception. - if (!$ignoreError && null === $metaModels) { - throw new \RuntimeException('Could not find the MetaModels with the name ' . $identifier); + foreach ($this->buildUrlsForItems($items, $renderSetting, $pageDetails) as $url) { + yield $url; + } + } finally { + // Reset language. + $GLOBALS['TL_LANGUAGE'] = $currentLanguage; + if (isset($prevLanguage) && $metaModel instanceof ITranslatedMetaModel) { + $metaModel->selectLanguage($prevLanguage); + } } - - return $metaModels; } /** - * Get a filter based on the id. + * Get all published index configs. * - * @param mixed $identifier Id of the filter. + * @return Generator * - * @return IFilterSettingCollection The filter + * @throws Exception When a database error occur. */ - protected function getFilterSettings($identifier): IFilterSettingCollection + private function getPublishedConfigs(): Generator { - return $this->filterSettingFactory->createCollection($identifier); + $statement = $this->connection + ->createQueryBuilder() + ->select('t.*') + ->from('tl_metamodel_searchable_pages', 't') + ->where('t.published=1') + ->executeQuery(); + + /** @var TSearchablePageConfig $config */ + foreach ($statement->fetchAllAssociative() as $config) { + yield $config; + } } /** - * Get the view for a MetaModels. + * Calculate list filter. * - * @param string|int $identifier ID/Name of the MetaModels. - * @param int $view ID of the view. + * @param IMetaModel $metaModel The MetaModel. + * @param string $filterId The id of the filter. + * @param array $presets The parameter preset values to use. * - * @return IRenderSettingCollection + * @return IFilter */ - protected function getView($identifier, $view): ?IRenderSettingCollection + private function getListFilter(IMetaModel $metaModel, string $filterId, array $presets): IFilter { - $metaModel = $this->getMetaModel($identifier, false); - if (null === $metaModel) { - return null; + $filterSettings = $this->filterSettingFactory->createCollection($filterId); + $presetNames = $filterSettings->getParameters(); + $processed = []; + + // We have to use all the preset values we want first. + foreach ($presets as $presetName => $arrPreset) { + if (in_array($presetName, $presetNames, true)) { + $processed[$presetName] = $arrPreset['value']; + } } - return $this->renderSettingFactory->createCollection($metaModel, $view); + // Create a new filter for the search. + $filter = $metaModel->getEmptyFilter(); + $filterSettings->addRules($filter, $processed); + + return $filter; } /** - * Get the language. - * - * First check the overwrite language. Then check if the MetaModels is translated and get all languages from it. - * Use the current language as fallback. + * Get a MetaModels by name or id. * - * @param string $singleLanguage The language with the overwrite. - * @param IMetaModel $metaModels The MetaModels for the check. + * @param string $identifier The Name/ID of a MetaModels. * - * @return string[] A list with all languages or null. + * @return IMetaModel|null * - * @SuppressWarnings(PHPMD.Superglobals) - * @SuppressWarnings(PHPMD.CamelCaseVariableName) + * @throws RuntimeException When the MetaModels is missing. */ - protected function getLanguage($singleLanguage, $metaModels): array + private function getMetaModel(string $identifier): ?IMetaModel { - if (!empty($singleLanguage)) { - return array($singleLanguage); - } - - if ($metaModels instanceof ITranslatedMetaModel) { - return $metaModels->getLanguages(); - } - - if ($metaModels->isTranslated() && $metaModels->getAvailableLanguages()) { - return $metaModels->getAvailableLanguages(); - } + // Translate id to name. + $identifier = $this->factory->translateIdToMetaModelName($identifier); - return array(\str_replace('-', '_', $GLOBALS['TL_LANGUAGE'])); + return $this->factory->getMetaModel($identifier); } /** - * Get the list of jumpTos based on the items. + * Build URL for every item to detail page. * - * @param IMetaModel $metaModels The MetaModels to be used. - * @param IFilter $filter The filter to be used. - * @param IRenderSettingCollection $view The view to be used. - * @param string|null $rootPage The root page id or null if there is no root page. + * @param IItems $items The items to process. + * @param IRenderSettingCollection $renderSetting The render settings for the detail page. + * @param array $pageDetails The page information for the detail page. * - * @return array A list of urls for the jumpTos - * - * @SuppressWarnings(PHPMD.Superglobals) - * @SuppressWarnings(PHPMD.CamelCaseVariableName) - * @SuppressWarnings(PHPMD.CyclomaticComplexity) - * @SuppressWarnings(PHPMD.NPathComplexity) + * @return Generator */ - protected function getJumpTosFor($metaModels, $filter, $view, $rootPage = null): array - { - $entries = []; - $filterAttributes = []; - $translated = ($metaModels instanceof ITranslatedMetaModel) || $metaModels->isTranslated(false); - $desired = \str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); - $fallback = $translated - ? (($metaModels instanceof ITranslatedMetaModel) ? $metaModels->getMainLanguage() - : $metaModels->getFallbackLanguage()) : null; - - foreach ((array) $view->get('jumpTo') as $jumpTo) { - $langCode = $jumpTo['langcode']; - // If either desired language or fallback, keep the result. - if (!$translated || ($langCode === $desired) || ($langCode === $fallback)) { - $jumpToFilterSetting = $this->filterSettingFactory->createCollection($jumpTo['filter']); - $filterAttributes = $jumpToFilterSetting->getReferencedAttributes(); - // If the desired language, break. - // Otherwise try to get the desired one until all have been evaluated. - if (!$translated || ($desired === $jumpTo['langcode'])) { - break; - } - } - } - - // Get the object. - /** @var Item $item */ - foreach ($metaModels->findByFilter( - $filter, - '', - 0, - 0, - 'ASC', - array_merge($view->getSettingNames(), $filterAttributes) - ) as $item) { - $jumpTo = $item->buildJumpToLink($view); - $event = new GetPageDetailsEvent((int) $jumpTo['page']); - $this->dispatcher->dispatch($event, ContaoEvents::CONTROLLER_GET_PAGE_DETAILS); - $pageDetails = $event->getPageDetails(); - - // If there is a root page check the context or if we have no page continue. - if (empty($pageDetails) || ($rootPage !== null && $pageDetails['rootId'] != $rootPage)) { - continue; - } - - $url = UrlBuilder::fromUrl($jumpTo['url']); + private function buildUrlsForItems( + IItems $items, + IRenderSettingCollection $renderSetting, + array $pageDetails + ): Generator { + foreach ($items as $item) { + assert($item instanceof Item); + $jumpTo = $item->buildJumpToLink($renderSetting); + $url = UrlBuilder::fromUrl($jumpTo['url']); if (null === $url->getScheme()) { // Build the absolute url. $url = $this->getBaseUrl($pageDetails, $jumpTo['url']); } - $entries[] = $url->getUrl(); + yield $url->getUrl(); } - - return $entries; } /** * Get the base URL. * - * @param string[] $pageDetails The page details. - * @param null|string $path Additional path settings. - * @param bool $ignoreSSL If active the system will ignore the 'rootUseSSL' flag. + * @param array $pageDetails The page details. + * @param string|null $path Additional path settings. * * @return UrlBuilder */ - private function getBaseUrl($pageDetails, $path = null, $ignoreSSL = false): UrlBuilder + private function getBaseUrl(array $pageDetails, string $path = null): UrlBuilder { $url = new UrlBuilder(); - // Set the domain (see contao/core#6421) - if ($pageDetails['domain']) { - $url->setHost($pageDetails['domain']); - } else { - $url->setHost(\Environment::get('host')); - } - - if ($pageDetails['rootUseSSL'] && !$ignoreSSL) { - $url->setScheme('https'); - } else { - $url->setScheme('http'); - } - - // Make an array for the parts. - $fullPath = []; - $fullPath[] = TL_PATH; + $url->setHost($pageDetails['domain'] ?: Environment::get('host')); + $url->setScheme($pageDetails['rootUseSSL'] ? 'https' : 'http'); // Get the path. if ($path === null) { - $event = new GenerateFrontendUrlEvent($pageDetails, null, $pageDetails['language'], true); + // Add dummy parameter, because non legacy mode parameter must not be null. + $event = new GenerateFrontendUrlEvent( + $pageDetails, + ((bool) ($pageDetails['requireItem'] ?? false)) ? '/foo/bar' : null, + $pageDetails['language'], + true + ); $this->dispatcher->dispatch($event, ContaoEvents::CONTROLLER_GENERATE_FRONTEND_URL); - $fullPath[] = $event->getUrl(); + $url->setPath($event->getUrl()); } else { - $fullPath[] = $path; + $url->setPath($path); } - $url->setPath(implode('/', $fullPath)); - return $url; } /** - * Remove all empty detail pages. + * Remove the plain details URL from the sitemap to omit 404 without filter url. * - * @param array $jumpTos A list with the jumpTo pages. + * @param array $pageDetails The page details information. + * @param DOMElement $urlSet The list of URLs. * * @return void */ - protected function removeEmptyDetailPages($jumpTos): void + public function removePlainDetailsUrl(array $pageDetails, DOMElement $urlSet): void { - // Remove the detail pages. - foreach ($jumpTos as $jumpTo) { - // Get the page from the url. - $event = new GetPageDetailsEvent((int) $jumpTo['value']); - $this->dispatcher->dispatch($event, ContaoEvents::CONTROLLER_GET_PAGE_DETAILS); - - $pageDetails = $event->getPageDetails(); - - // Check if we have a page - if not go to the next one. - if (empty($pageDetails)) { + $removeUrl = $this->getBaseUrl($pageDetails)->getUrl(); + foreach ($urlSet->childNodes as $childNode) { + if (!$this->isDomElement($childNode, 'url')) { continue; } - // Make a full url from it. - $baseUrl = $this->getBaseUrl($pageDetails); - - if (($strKey = array_search($baseUrl->getUrl(), $this->foundPages)) !== false) { - unset($this->foundPages[$strKey]); - } - - // Make a full url from it without the https. - $baseUrl = $this->getBaseUrl($pageDetails, null, true); - - if (($strKey = array_search($baseUrl->getUrl(), $this->foundPages)) !== false) { - unset($this->foundPages[$strKey]); - } - } - } - - /** - * Set parameters. - * - * @param string $filterId The id of the filter. - * @param string[] $presets The parameter preset values to use. - * @param string[] $values The dynamic parameter values that may be used. - * - * @return array - */ - public function setFilterParameters($filterId, $presets, $values) - { - $filterSettings = $this->getFilterSettings($filterId); - $presetNames = $filterSettings->getParameters(); - $feFilterParams = array_keys($filterSettings->getParameterFilterNames()); - $processed = []; - - // We have to use all the preset values we want first. - foreach ($presets as $strPresetName => $arrPreset) { - if (in_array($strPresetName, $presetNames, true)) { - $processed[$strPresetName] = $arrPreset['value']; - } - } + foreach ($childNode->childNodes as $childNode2) { + if (!$this->isDomElement($childNode2, 'loc') || trim((string) $childNode2->nodeValue) !== $removeUrl) { + continue; + } + assert($childNode->parentNode instanceof \DOMNode); + $childNode->parentNode->removeChild($childNode); - // Now we have to use all FE filter params, that are either: - // * not contained within the presets - // * or are overridable. - foreach ($feFilterParams as $strParameter) { - // Unknown parameter? - next please. - if (!array_key_exists($strParameter, $values)) { - continue; - } - // Not a preset or allowed to override? - use value. - if ((!array_key_exists($strParameter, $presets)) || $presets[$strParameter]['use_get']) { - $processed[$strParameter] = $values[$strParameter]; + return; } } - - return $processed; } /** - * Start point for the hook getSearchablePages. - * - * @param array $pages List with all pages. - * @param int|null $rootPage ID of the root page. - * @param bool|null $fromSiteMap True when called from sitemap generator, null otherwise. - * @param string|null $language The current language. - * - * @return array - * - * @throws \Doctrine\DBAL\DBALException When an database error occur. + * Check if right DOM element. * - * @see \RebuildIndex::run() - * @see \Automator::generateSitemap() + * @param DOMNode $node The node. + * @param string $nodeName The node name. * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @return bool */ - public function addPages($pages, $rootPage = null, $fromSiteMap = false, $language = null) + private function isDomElement(DOMNode $node, string $nodeName): bool { - // Save the pages. - $this->foundPages = $pages; - unset($pages); - - // Run each entry in the published config array. - foreach ($this->getConfigs() as $config) { - if (!$config['published']) { - continue; - } - $this->getMetaModelsPages( - $config, - $rootPage, - $language - ); - } - - asort($this->foundPages); - - return $this->foundPages; - } - - /** - * Get a MetaModels, a filter and a renderSetting. Get all items based on the filter and build the jumpTo urls. - * - * @param array $config ID of the MetaModels. - * @param string|null $rootPage The root page id or null if there is no root page. - * @param string|null $language The current language. - * - * @return void - * - * @SuppressWarnings(PHPMD.Superglobals) - * @SuppressWarnings(PHPMD.CamelCaseVariableName) - */ - private function getMetaModelsPages( - $config, - $rootPage = null, - $language = null - ): void { - $metaModelsIdentifier = $config['pid']; - $filterIdentifier = $config['filter']; - $presetParams = StringUtil::deserialize($config['filterparams'], true); - $renderSettingId = $config['rendersetting']; - - // Get the MetaModels. - $metaModels = $this->getMetaModel($metaModelsIdentifier, false); - $availableLanguages = $this->getLanguage($language, $metaModels); - $currentLanguage = $GLOBALS['TL_LANGUAGE']; - - $foundPages = [$this->foundPages]; - - foreach ($availableLanguages as $newLanguage) { - // Change language. - $GLOBALS['TL_LANGUAGE'] = \str_replace('_', '-', $newLanguage); - if ($metaModels instanceof ITranslatedMetaModel) { - $prevLanguage = $metaModels->selectLanguage($newLanguage); - } - - // Get the view. - $view = $this->getView($metaModelsIdentifier, $renderSettingId); - $jumpTos = $view->get('jumpTo'); - - // Set the filter. - $processed = $this->setFilterParameters($filterIdentifier, $presetParams, []); - - // Create a new filter for the search. - $filter = $metaModels->getEmptyFilter(); - $filterSetting = $this->getFilterSettings($filterIdentifier); - $filterSetting->addRules($filter, $processed); - - // Get all jumpTos. - $newEntries = $this->getJumpTosFor($metaModels, $filter, $view, $rootPage); - - // Remove all empty page details. - $this->removeEmptyDetailPages($jumpTos); - - // Reset language. - $GLOBALS['TL_LANGUAGE'] = $currentLanguage; - if ($metaModels instanceof ITranslatedMetaModel) { - $metaModels->selectLanguage($prevLanguage); - } - - // Merge all results. - $foundPages[] = $newEntries; - } - - $this->foundPages = array_merge(...$foundPages); - - // Reset the language. - $GLOBALS['TL_LANGUAGE'] = $currentLanguage; + return ($node instanceof DOMElement) && $node->nodeName === $nodeName; } } diff --git a/src/CoreBundle/EventListener/InsertTagsListener.php b/src/CoreBundle/EventListener/InsertTagsListener.php index ca8005a0b..5ecbacd5a 100644 --- a/src/CoreBundle/EventListener/InsertTagsListener.php +++ b/src/CoreBundle/EventListener/InsertTagsListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,78 +17,89 @@ * @author David Molineus * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\CoreBundle\EventListener; -use Contao\CoreBundle\ServiceAnnotation\Hook; use Contao\StringUtil; use Contao\Input; -use Contao\System; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\FetchMode; +use Doctrine\DBAL\Exception; +use MetaModels\Attribute\IAttribute; use MetaModels\Filter\Rules\StaticIdList; use MetaModels\Filter\Setting\IFilterSettingFactory; use MetaModels\IFactory; use MetaModels\IMetaModel; use MetaModels\ItemList; use MetaModels\Render\Setting\IRenderSettingFactory; -use Terminal42\ServiceAnnotationBundle\ServiceAnnotationInterface; +use Psr\Log\LoggerInterface; /** * This class handles the replacement of all MetaModels insert tags. * * @codingStandardsIgnoreStart + * * Available insert tags: * * -- Total Count -- * mm::total::mod::[ID] * mm::total::ce::[ID] - * mm::total::mm::[MM Name|ID](::[ID filter]) + * mm::total::mm::[MM Table-Name|ID](::[ID filter]) * * -- Item -- - * mm::item::[MM Name|ID]::[Item ID|ID,ID,ID]::[ID render setting](::[Output raw|text|html|..]) + * mm::item::[MM Table-Name|ID]::[Item ID|ID,ID,ID]::[ID render setting](::[Output (Default:text)|html5]) * * -- Attribute -- - * mm::attribute::[MM Name|ID]::[Item ID]::[Attribute Name|ID](::[Output raw|text|html|..]) + * mm::attribute::[MM Table-Name|ID]::[Item ID]::[ID render setting]::[Attribute Col-Name|ID](::[Output (Default:text)|html5|raw]) * * -- JumpTo -- - * mm::jumpTo::[MM Name|ID]::[Item ID]::[ID render setting](::[Parameter (Default:url)|label|page|params.attname]) + * mm::jumpTo::[MM Table-Name|ID]::[Item ID]::[ID render setting](::[Parameter (Default:url)|label|page|params.attname]) * * @codingStandardsIgnoreEnd + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ -class InsertTagsListener implements ServiceAnnotationInterface +final class InsertTagsListener { /** * Database connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * The render setting factory. * * @var IRenderSettingFactory */ - private $renderSettingFactory; + private IRenderSettingFactory $renderSettingFactory; /** * The filter setting factory. * * @var IFilterSettingFactory */ - private $filterSettingFactory; + private IFilterSettingFactory $filterSettingFactory; + + /** + * The logger interface. + * + * @var LoggerInterface|null + */ + private ?LoggerInterface $logger; /** * InsertTagsListener constructor. @@ -97,17 +108,20 @@ class InsertTagsListener implements ServiceAnnotationInterface * @param IFactory $factory The MetaModels factory. * @param IRenderSettingFactory $renderSettingFactory The render setting factory. * @param IFilterSettingFactory $filterSettingFactory The filter setting factory. + * @param LoggerInterface|null $logger The logger interface. */ public function __construct( Connection $connection, IFactory $factory, IRenderSettingFactory $renderSettingFactory, - IFilterSettingFactory $filterSettingFactory + IFilterSettingFactory $filterSettingFactory, + LoggerInterface $logger = null ) { $this->connection = $connection; $this->factory = $factory; $this->renderSettingFactory = $renderSettingFactory; $this->filterSettingFactory = $filterSettingFactory; + $this->logger = $logger; } /** @@ -115,13 +129,11 @@ public function __construct( * * @param string $insertTag The tag to evaluate. * - * @return bool|string - * - * @Hook("replaceInsertTags") + * @return bool|int|string */ - public function __invoke($insertTag) + public function __invoke(string $insertTag): bool|int|string { - $elements = explode('::', $insertTag); + $elements = \explode('::', $insertTag); // Check if we have the mm tags. if ('mm' !== $elements[0]) { @@ -133,23 +145,40 @@ public function __invoke($insertTag) switch ($elements[1]) { // Count for mod or ce elements. case 'total': - return $this->getCount($elements[2], $elements[3], $elements[4]); + return $this->checkMinExpectElements(4, $elements) + ? $this->getCount($elements[2], $elements[3], (isset($elements[4]) ? (int) $elements[4] : null)) + : false; // Get value from an attribute. case 'attribute': - return $this->getAttribute($elements[2], $elements[3], $elements[4], $elements[5]); + return $this->checkMinExpectElements(6, $elements) + ? $this->getAttribute( + $elements[2], + $elements[3], + $elements[4], + $elements[5], + ($elements[6] ?? null) + ) + : false; // Get item. case 'item': - return $this->getItem($elements[2], $elements[3], $elements[4], $elements[5]); + return $this->checkMinExpectElements(5, $elements) + ? $this->getItem($elements[2], $elements[3], $elements[4], ($elements[5] ?? null)) + : false; + // Get jump-to detail page. case 'jumpTo': - return $this->jumpTo($elements[2], $elements[3], $elements[4], $elements[5]); + return $this->checkMinExpectElements(5, $elements) + ? $this->jumpTo($elements[2], $elements[3], $elements[4], ($elements[5] ?? null)) + : false; default: } } catch (\Exception $exc) { - System::log('Error by replace tags: ' . $exc->getMessage(), __CLASS__ . ' | ' . __FUNCTION__, TL_ERROR); + $this->logger?->error( + 'Error by replace tags: ' . $exc->getMessage() . ' | ' . __CLASS__ . ' | ' . __FUNCTION__ + ); } return false; @@ -158,33 +187,29 @@ public function __invoke($insertTag) /** * Get the jumpTo for a chosen value. * - * @param string|int $mixMetaModel ID or name of MetaModels. - * - * @param int $mixDataId ID of the data row. - * - * @param int $viewId ID of render setting. + * @param string $mixMetaModel ID or column name of MetaModels. + * @param string $mixDataId ID of the data row. + * @param string $viewId ID of render setting. + * @param string|null $strParam Name of parameter - (Default:url)|label|page|params.[attrname]. * - * @param string $strParam Name of parameter - Default:url|label|page|params.[attrname]. - * - * @return boolean|string Return false when nothing was found for the requested value. + * @return bool|string Return false when nothing was found for the requested value. */ - protected function jumpTo($mixMetaModel, $mixDataId, $viewId, $strParam = 'url') - { + private function jumpTo( + string $mixMetaModel, + string $mixDataId, + string $viewId, + ?string $strParam + ): bool|string { // Set the param to url if empty. - if (empty($strParam)) { + if (null === $strParam || '' === $strParam) { $strParam = 'url'; } // Get the MetaModel. Return if we can not find one. $metaModel = $this->loadMetaModel($mixMetaModel); - if (null === $metaModel) { - return false; - } // Get the render setting. - if (null === $renderSettings = $this->renderSettingFactory->createCollection($metaModel, $viewId)) { - return false; - } + $renderSetting = $this->renderSettingFactory->createCollection($metaModel, $viewId); // Get the data row. $item = $metaModel->findById($mixDataId); @@ -193,7 +218,7 @@ protected function jumpTo($mixMetaModel, $mixDataId, $viewId, $strParam = 'url') } // Render the item and check if we have a jump to. - $arrRenderedItem = $item->parseValue('text', $renderSettings); + $arrRenderedItem = $item->parseValue('text', $renderSetting); if (!isset($arrRenderedItem['jumpTo'])) { return false; } @@ -201,7 +226,7 @@ protected function jumpTo($mixMetaModel, $mixDataId, $viewId, $strParam = 'url') // Check if someone want the sub params. if (stripos($strParam, 'params.') !== false) { $mixAttName = StringUtil::trimsplit('.', $strParam); - $mixAttName = array_pop($mixAttName); + $mixAttName = \array_pop($mixAttName); if (isset($arrRenderedItem['jumpTo']['params'][$mixAttName])) { return $arrRenderedItem['jumpTo']['params'][$mixAttName]; @@ -218,110 +243,132 @@ protected function jumpTo($mixMetaModel, $mixDataId, $viewId, $strParam = 'url') /** * Get an item. * - * @param string|int $metaModelIdOrName ID or name of MetaModels. - * @param string|int $mixDataId ID of the data row. - * @param int $viewId ID of render setting. - * @param string $strOutput Name of output. Default:null (fallback to html5)|text|html5|... + * @param int|string $metaModelIdOrName ID or column name of MetaModels. + * @param string $mixDataId ID or list of IDs of the data row. + * @param string $viewId ID of render setting. + * @param string|null $outputFormat Name of output format- (Default:text)|html5. * - * @return boolean|string Return false when nothing was found or return the value. + * @return bool|string Return false when nothing was found or return the value. */ - protected function getItem($metaModelIdOrName, $mixDataId, $viewId, $strOutput = null) - { + private function getItem( + int|string $metaModelIdOrName, + string $mixDataId, + string $viewId, + ?string $outputFormat + ): bool|string { // Get the MetaModel. Return if we can not find one. $metaModel = $this->loadMetaModel($metaModelIdOrName); - if (null === $metaModel) { - return false; - } - // Set output to default if not set. - if (empty($strOutput)) { - $strOutput = 'html5'; + // Set output format to default if not set. + if (null === $outputFormat || '' === $outputFormat) { + $outputFormat = 'text'; } $objMetaModelList = new ItemList(); $objMetaModelList - ->setMetaModel($metaModel->get('id'), $viewId) - ->overrideOutputFormat($strOutput); + ->setMetaModel((string) $metaModel->get('id'), $viewId) + ->overrideOutputFormat($outputFormat); // Handle a set of ids. + /** @var list $arrIds */ $arrIds = StringUtil::trimsplit(',', $mixDataId); - // Render an empty insert tag rather than displaying a list with an empty. - // result information. do not return false here because the insert tag itself is correct. - if (count($arrIds) < 1) { + // Render an empty insert tag rather than displaying a list with an empty + // result information - do not return false here because the insert tag itself is correct. + if (\count($arrIds) < 1) { return ''; } $objMetaModelList->addFilterRule(new StaticIdList($arrIds)); + return $objMetaModelList->render(false, $this); } /** * Get from MM X the item with the id Y and parse the attribute Z and return it. * - * @param string|int $metaModelIdOrName ID or name of MetaModel. - * @param int $intDataId ID of the data row. - * @param string $strAttributeName Name of the attribute. - * @param string $strOutput Name of output. Default:raw|text|html5|... + * @param string $metaModelIdOrName ID or column name of MetaModel. + * @param string $intDataId ID of the data row. + * @param string $viewId ID of render setting. + * @param string $attributeIdentifier ID or column name of the attribute. + * @param string|null $outputFormat Type of output format - (Default:text)|html5|raw. * - * @return boolean|string Return false when nothing was found or return the value. + * @return bool|string Return false when nothing was found or return the value. * - * @throws \RuntimeException If $intDataId does not provide an existingMetaModel ID. */ - protected function getAttribute($metaModelIdOrName, $intDataId, $strAttributeName, $strOutput = 'raw') - { + private function getAttribute( + string $metaModelIdOrName, + string $intDataId, + string $viewId, + string $attributeIdentifier, + ?string $outputFormat + ): bool|string { // Get the MM. - $objMM = $this->loadMetaModel($metaModelIdOrName); - if (null === $objMM) { + $metaModel = $this->loadMetaModel($metaModelIdOrName); + + // Get item. + $item = $metaModel->findById($intDataId); + if (null === $item) { return false; } - // Set output to default if not set. - if (empty($strOutput)) { - $strOutput = 'raw'; + if (\is_numeric($attributeIdentifier)) { + $attribute = $metaModel->getAttributeById((int) $attributeIdentifier); + assert($attribute instanceof IAttribute); + $attributeIdentifier = $attribute->getColName(); } - // Get item. - $objMetaModelItem = $objMM->findById($intDataId); - if (null === $objMetaModelItem) { - throw new \RuntimeException('MetaModel item not found: ' . $intDataId); + $originalOutputFormat = $outputFormat; + // Set output format to default if not set or raw. + if (null === $outputFormat || '' === $outputFormat || 'raw' === $outputFormat) { + $outputFormat = 'text'; } + // Get render setting. + $renderSetting = $this->renderSettingFactory->createCollection($metaModel, $viewId); + // Parse attribute. - $arrAttr = $objMetaModelItem->parseAttribute($strAttributeName); + $arrAttr = $item->parseAttribute($attributeIdentifier, $outputFormat, $renderSetting); + + // Reset format to raw if is it. + if ('raw' === $originalOutputFormat) { + $outputFormat = 'raw'; + } - return $arrAttr[$strOutput]; + return $arrAttr[$outputFormat] ?? false; } /** * Get count from a module or content element of a mm or from mm with filter direct. * - * @param string $type Type of element like mod or ce. - * @param string|int $identifier ID of content element or module or ID or name of MetaModel. + * @param string $type Type of element like mod, ce or mm. + * @param int|string $identifier ID of content element or module or ID or name of MetaModel. * @param int|null $filterId ID of the filter. * * @return int Return the count value. + * @throws Exception */ - protected function getCount(string $type, $identifier, int $filterId = null): int + private function getCount(string $type, int|string $identifier, ?int $filterId = null): int { switch ($type) { // From module, can be a MetaModel list or filter. case 'mod': - if (null !== ($result = $this->getMetaModelDataFrom('tl_module', $identifier))) { - return $this->getCountFor($result->metamodel, $result->metamodel_filtering); + if (false !== ($result = $this->getMetaModelDataFrom('tl_module', (int) $identifier))) { + return $this->getCountFor($result['metamodel'], $result['metamodel_filtering']); } break; // From content element, can be a MetaModel list or filter. case 'ce': - if (null !== ($result = $this->getMetaModelDataFrom('tl_content', $identifier))) { - return $this->getCountFor($result->metamodel, $result->metamodel_filtering); + if (false !== ($result = $this->getMetaModelDataFrom('tl_content', (int) $identifier))) { + return $this->getCountFor($result['metamodel'], $result['metamodel_filtering']); } break; // From MetaModel with filter. case 'mm': - return $this->getCountFor($identifier, $filterId); + return $this->getCountFor((string) $identifier, $filterId); + break; // Unknown element type. default: @@ -334,26 +381,26 @@ protected function getCount(string $type, $identifier, int $filterId = null): in /** * Try to load the MetaModel by id or name. * - * @param mixed $nameOrId Name or id of the MetaModel. + * @param int|string $nameOrId Name or id of the MetaModel. * - * @return IMetaModel|null + * @return IMetaModel */ - protected function loadMetaModel($nameOrId): ?IMetaModel + private function loadMetaModel(int|string $nameOrId): IMetaModel { - if (is_numeric($nameOrId)) { + // Name. + $tableName = $nameOrId; + if (\is_numeric($nameOrId)) { // ID. - $tableName = $this->factory->translateIdToMetaModelName($nameOrId); - } elseif (is_string($nameOrId)) { - // Name. - $tableName = $nameOrId; + $tableName = $this->factory->translateIdToMetaModelName((string) $nameOrId); } - if (isset($tableName)) { - return $this->factory->getMetaModel($tableName); + $metaModel = $this->factory->getMetaModel((string) $tableName); + + if (null === $metaModel) { + throw new \RuntimeException('MetaModel not found: ' . $nameOrId); } - // Unknown. - return null; + return $metaModel; } /** @@ -362,52 +409,49 @@ protected function loadMetaModel($nameOrId): ?IMetaModel * @param string $strTable Name of table. * @param int $intID ID of the filter. * - * @return null|\stdClass Returns null when nothing was found or a \Database\Result with the chosen information. + * @return false|array Returns null when nothing was found or a \Database\Result with the chosen information. * - * @throws \Doctrine\DBAL\DBALException When an database error occur. + * @throws Exception */ - protected function getMetaModelDataFrom($strTable, $intID) + private function getMetaModelDataFrom(string $strTable, int $intID): bool|array { // Check if we know the table. - if (!$this->connection->getSchemaManager()->tablesExist([$strTable])) { - return null; + if (!$this->connection->createSchemaManager()->tablesExist([$strTable])) { + return false; } // Get all information form table or return null if we have no data. $statement = $this->connection ->createQueryBuilder() - ->select('t.metamodel, t.metamodel_filtering') + ->select('t.metamodel', 't.metamodel_filtering') ->from($strTable, 't') ->where('t.id=:id') ->setParameter('id', $intID) - ->execute(); + ->executeQuery(); // Check if we have some data. if ($statement->rowCount() < 1) { - return null; + return false; } - return $statement->fetch(FetchMode::STANDARD_OBJECT); + return $statement->fetchAssociative(); } /** * Get count form one MM for chosen filter. * - * @param int $intMetaModelId ID of the metamodels. - * @param int|null $intFilterId ID of the filter. + * @param string $metaModelNameOrId Name or id of the MetaModel. + * @param int|null $intFilterId ID of the filter. * * @return int The count result. */ - protected function getCountFor(int $intMetaModelId, int $intFilterId = null): int + private function getCountFor(string $metaModelNameOrId, ?int $intFilterId = null): int { - $metaModel = $this->loadMetaModel($intMetaModelId); - if (null === $metaModel) { - return 0; - } + $metaModel = $this->loadMetaModel($metaModelNameOrId); $objFilter = $metaModel->getEmptyFilter(); - if ($intFilterId) { - $collection = $this->filterSettingFactory->createCollection($intFilterId); + if (null !== $intFilterId) { + $collection = $this->filterSettingFactory->createCollection((string) $intFilterId); $values = []; foreach ($collection->getParameters() as $key) { @@ -420,21 +464,30 @@ protected function getCountFor(int $intMetaModelId, int $intFilterId = null): in return $metaModel->getCount($objFilter); } + /** + * @param int $expectCount The expected number of elements. + * @param array $elements The elements. + * + * @return bool + */ + private function checkMinExpectElements(int $expectCount, array $elements): bool + { + return \count($elements) >= $expectCount; + } + /** * Check if the item is published. * - * @param IMetaModel $metaModel Current metamodels. - * @param int $intItemId Id of the item. + * @param IMetaModel $metaModel Current MetaModel. + * @param int $intItemId ID of the item. * - * @return boolean True => Published | False => Not published + * @return bool True => Published | False => Not published * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) * @deprecated * - * @throws \Doctrine\DBAL\DBALException When a database error occur. - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - protected function isPublishedItem($metaModel, $intItemId): bool + protected function isPublishedItem(IMetaModel $metaModel, int $intItemId): bool { // @codingStandardsIgnoreStart @trigger_error( diff --git a/src/CoreBundle/EventListener/PurgeListener.php b/src/CoreBundle/EventListener/PurgeListener.php index e30776177..b32ce81cb 100644 --- a/src/CoreBundle/EventListener/PurgeListener.php +++ b/src/CoreBundle/EventListener/PurgeListener.php @@ -59,7 +59,8 @@ public function __construct(PurgeCache $purger) public function checkPurge(AbstractModelAwareEvent $event) { $table = $event->getModel()->getProviderName(); - if (($table == 'tl_metamodel') || + if ( + ($table == 'tl_metamodel') || ($table == 'tl_metamodel_dca') || ($table == 'tl_metamodel_dca_sortgroup') || ($table == 'tl_metamodel_dcasetting') || diff --git a/src/CoreBundle/EventListener/SubSystemBootListener.php b/src/CoreBundle/EventListener/SubSystemBootListener.php index c2c476afa..5b8487dd2 100644 --- a/src/CoreBundle/EventListener/SubSystemBootListener.php +++ b/src/CoreBundle/EventListener/SubSystemBootListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author David Molineus * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2012-2021 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -36,41 +37,40 @@ */ class SubSystemBootListener { - /** * The Contao framework. * * @var ContaoFramework */ - private $contaoFramework; + private ContaoFramework $contaoFramework; /** * The database connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * The logger. * * @var LoggerInterface */ - private $logger; + private LoggerInterface $logger; /** * The scope matcher. * * @var RequestScopeDeterminator */ - private $scopeMatcher; + private RequestScopeDeterminator $scopeMatcher; /** * The event dispatcher. * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * SubSystemBoot constructor. @@ -102,7 +102,7 @@ public function __construct( */ public function boot(): void { - /** @var Environment $environment */ + /** @psalm-suppress InternalMethod - the ContaoFramework class is internal, not the method usage. */ $environment = $this->contaoFramework->getAdapter(Environment::class); $script = explode('?', $environment->get('relativeRequest'), 2)[0]; @@ -112,21 +112,23 @@ public function boot(): void } try { - if (!$this->connection->getSchemaManager()->tablesExist( - [ - 'tl_metamodel', - 'tl_metamodel_dca', - 'tl_metamodel_dca_sortgroup', - 'tl_metamodel_dcasetting', - 'tl_metamodel_dcasetting_condition', - 'tl_metamodel_attribute', - 'tl_metamodel_filter', - 'tl_metamodel_filtersetting', - 'tl_metamodel_rendersettings', - 'tl_metamodel_rendersetting', - 'tl_metamodel_dca_combine', - ] - )) { + if ( + !$this->connection->createSchemaManager()->tablesExist( + [ + 'tl_metamodel', + 'tl_metamodel_dca', + 'tl_metamodel_dca_sortgroup', + 'tl_metamodel_dcasetting', + 'tl_metamodel_dcasetting_condition', + 'tl_metamodel_attribute', + 'tl_metamodel_filter', + 'tl_metamodel_filtersetting', + 'tl_metamodel_rendersettings', + 'tl_metamodel_rendersetting', + 'tl_metamodel_dca_combine', + ] + ) + ) { $this->logger->error('MetaModels startup interrupted. Not all MetaModels tables have been created.'); return; } diff --git a/src/CoreBundle/EventListener/UserListener.php b/src/CoreBundle/EventListener/UserListener.php index 1b865a07e..a180ca03e 100644 --- a/src/CoreBundle/EventListener/UserListener.php +++ b/src/CoreBundle/EventListener/UserListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,7 +24,7 @@ use Contao\CoreBundle\Routing\ScopeMatcher; use MetaModels\BackendIntegration\Module; use MetaModels\ViewCombination\ViewCombination; -use Symfony\Component\HttpKernel\Event\GetResponseEvent; +use Symfony\Component\HttpKernel\Event\RequestEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationTrustResolverInterface; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; @@ -38,68 +38,68 @@ class UserListener * * @var TokenStorageInterface */ - private $tokenStorage; + private TokenStorageInterface $tokenStorage; /** * The authentication resolver. * * @var AuthenticationTrustResolverInterface */ - private $authenticationTrustResolver; + private AuthenticationTrustResolverInterface $trustResolver; /** * The scope matcher. * * @var ScopeMatcher */ - private $scopeMatcher; + private ScopeMatcher $scopeMatcher; /** * The view combination. * * @var ViewCombination */ - private $viewCombination; + private ViewCombination $viewCombination; /** * Constructor. * - * @param TokenStorageInterface $tokenStorage The token storage. - * @param AuthenticationTrustResolverInterface $authenticationTrustResolver The authentication resolver. - * @param ScopeMatcher $scopeMatcher The scope matche. - * @param ViewCombination $viewCombination The view combination. + * @param TokenStorageInterface $tokenStorage The token storage. + * @param AuthenticationTrustResolverInterface $trustResolver The authentication resolver. + * @param ScopeMatcher $scopeMatcher The scope matche. + * @param ViewCombination $viewCombination The view combination. */ public function __construct( TokenStorageInterface $tokenStorage, - AuthenticationTrustResolverInterface $authenticationTrustResolver, + AuthenticationTrustResolverInterface $trustResolver, ScopeMatcher $scopeMatcher, ViewCombination $viewCombination ) { - $this->tokenStorage = $tokenStorage; - $this->authenticationTrustResolver = $authenticationTrustResolver; - $this->scopeMatcher = $scopeMatcher; - $this->viewCombination = $viewCombination; + $this->tokenStorage = $tokenStorage; + $this->trustResolver = $trustResolver; + $this->scopeMatcher = $scopeMatcher; + $this->viewCombination = $viewCombination; } /** * Replaces the current session data with the stored session data. * - * @param GetResponseEvent $event The event. + * @param RequestEvent $event The event. * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) * * @return void */ - public function onKernelRequest(GetResponseEvent $event) + public function onKernelRequest(RequestEvent $event) { - if (!$this->scopeMatcher->isBackendMasterRequest($event)) { + if (!$this->scopeMatcher->isBackendMainRequest($event)) { return; } $token = $this->tokenStorage->getToken(); - if (null === $token || $this->authenticationTrustResolver->isAnonymous($token)) { + if (null === $token || !$this->trustResolver->isAuthenticated($token)) { return; } @@ -119,6 +119,7 @@ public function onKernelRequest(GetResponseEvent $event) */ private function buildBackendModules(&$localMenu) { + return; foreach ($this->viewCombination->getStandalone() as $metaModelName => $screen) { $section = $screen['meta']['backendsection']; if (!isset($localMenu[$section])) { @@ -128,7 +129,7 @@ private function buildBackendModules(&$localMenu) $localMenu[$section]['metamodel_' . $metaModelName] = ['tables' => []]; } $localMenu[$section]['metamodel_' . $metaModelName]['callback'] = Module::class; - array_unshift($localMenu[$section]['metamodel_' . $metaModelName]['tables'], $metaModelName); + \array_unshift($localMenu[$section]['metamodel_' . $metaModelName]['tables'], $metaModelName); $GLOBALS['TL_LANG']['MOD']['metamodel_' . $metaModelName] = [ ($screen['label'][$GLOBALS['TL_LANGUAGE']] ?? ($screen['label'][''] ?? '')) ]; @@ -145,12 +146,12 @@ private function buildBackendModules(&$localMenu) private function injectChildTables(&$localMenu) { $parented = $this->viewCombination->getParented(); - $lastCount = count($parented); + $lastCount = \count($parented); while ($parented) { foreach ($parented as $metaModelName => $child) { foreach ($localMenu as $groupName => $modules) { foreach ($modules as $moduleName => $module) { - if (isset($module['tables']) && in_array($child['meta']['ptable'], $module['tables'])) { + if (isset($module['tables']) && \in_array($child['meta']['ptable'], $module['tables'])) { $localMenu[$groupName][$moduleName]['tables'][] = $metaModelName; unset($parented[$metaModelName]); break; @@ -159,10 +160,10 @@ private function injectChildTables(&$localMenu) } } // If the dependencies can not be resolved any further, we give up here to prevent an endless loop. - if (count($parented) == $lastCount) { + if (\count($parented) === $lastCount) { break; } - $lastCount = count($parented); + $lastCount = \count($parented); } } } diff --git a/src/CoreBundle/LegacyCompat/ServiceContainerInitializer.php b/src/CoreBundle/LegacyCompat/ServiceContainerInitializer.php index 52d5dc4ae..b464aa833 100644 --- a/src/CoreBundle/LegacyCompat/ServiceContainerInitializer.php +++ b/src/CoreBundle/LegacyCompat/ServiceContainerInitializer.php @@ -35,7 +35,7 @@ class ServiceContainerInitializer * * @var ContainerInterface */ - private $container; + private ContainerInterface $container; /** * Create a new instance. @@ -53,6 +53,8 @@ public function __construct(ContainerInterface $container) * @param MetaModelsServiceContainer $serviceContainer The container to configure. * * @return MetaModelsServiceContainer + * + * @psalm-suppress DeprecatedClass */ public function configure(MetaModelsServiceContainer $serviceContainer) { diff --git a/src/CoreBundle/MetaModelsCoreBundle.php b/src/CoreBundle/MetaModelsCoreBundle.php index 6fec03470..2f2619730 100644 --- a/src/CoreBundle/MetaModelsCoreBundle.php +++ b/src/CoreBundle/MetaModelsCoreBundle.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. @@ -13,14 +13,19 @@ * @package MetaModels/core * @author Christian Schiffler * @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\CoreBundle; +use MetaModels\CoreBundle\DependencyInjection\CompilerPass\CollectDoctrineSchemaGeneratorsPass; use MetaModels\CoreBundle\DependencyInjection\CompilerPass\CollectFactoriesPass; +use MetaModels\CoreBundle\DependencyInjection\CompilerPass\CollectSchemaGeneratorsPass; +use MetaModels\CoreBundle\DependencyInjection\CompilerPass\CollectSchemaManagersPass; +use MetaModels\CoreBundle\DependencyInjection\CompilerPass\PrepareTranslatorPass; use Symfony\Component\DependencyInjection\ContainerBuilder; use Symfony\Component\HttpKernel\Bundle\Bundle; @@ -32,10 +37,14 @@ class MetaModelsCoreBundle extends Bundle /** * {@inheritDoc} */ - public function build(ContainerBuilder $container) + public function build(ContainerBuilder $container): void { parent::build($container); $container->addCompilerPass(new CollectFactoriesPass()); + $container->addCompilerPass(new CollectSchemaGeneratorsPass()); + $container->addCompilerPass(new CollectSchemaManagersPass()); + $container->addCompilerPass(new CollectDoctrineSchemaGeneratorsPass()); + $container->addCompilerPass(new PrepareTranslatorPass()); } } diff --git a/src/CoreBundle/Migration/FindClearAllTemplateMigration.php b/src/CoreBundle/Migration/FindClearAllTemplateMigration.php index 7cc96b153..066ae4cbf 100644 --- a/src/CoreBundle/Migration/FindClearAllTemplateMigration.php +++ b/src/CoreBundle/Migration/FindClearAllTemplateMigration.php @@ -18,7 +18,7 @@ * @filesource */ -declare(strict_types = 1); +declare(strict_types=1); namespace MetaModels\CoreBundle\Migration; diff --git a/src/CoreBundle/Migration/FindXhtmlTemplateMigration.php b/src/CoreBundle/Migration/FindXhtmlTemplateMigration.php index 2141de61b..e8824bf44 100644 --- a/src/CoreBundle/Migration/FindXhtmlTemplateMigration.php +++ b/src/CoreBundle/Migration/FindXhtmlTemplateMigration.php @@ -18,7 +18,7 @@ * @filesource */ -declare(strict_types = 1); +declare(strict_types=1); namespace MetaModels\CoreBundle\Migration; diff --git a/src/CoreBundle/Migration/SetDefaultZeroMigration.php b/src/CoreBundle/Migration/SetDefaultZeroMigration.php index 0b9c5aa55..f7b159790 100644 --- a/src/CoreBundle/Migration/SetDefaultZeroMigration.php +++ b/src/CoreBundle/Migration/SetDefaultZeroMigration.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 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. @@ -13,7 +13,7 @@ * @package MetaModels/core * @author Ingolf Steinhardt * @author Christian Schiffler - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +25,7 @@ use Contao\CoreBundle\Migration\AbstractMigration; use Contao\CoreBundle\Migration\MigrationResult; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use Doctrine\DBAL\Schema\Table; /** @@ -94,6 +95,7 @@ public function getName(): string * - default zero values for system columns values not set. * * @return bool + * @throws Exception */ public function shouldRun(): bool { @@ -109,6 +111,7 @@ public function shouldRun(): bool * Collect the columns to be updated and update them. * * @return MigrationResult + * @throws Exception */ public function run(): MigrationResult { @@ -121,13 +124,14 @@ public function run(): MigrationResult } } - return new MigrationResult(true, 'Adjusted column(s): ' . implode(', ', $message)); + return new MigrationResult(true, 'Adjusted column(s): ' . \implode(', ', $message)); } /** * Fetch all columns that are not nullable yet. * * @return array> + * @throws Exception */ private function fetchNonDefaultZeroColumns(): array { @@ -135,14 +139,14 @@ private function fetchNonDefaultZeroColumns(): array if (empty($tables)) { return []; } - $schemaManager = $this->connection->getSchemaManager(); + $schemaManager = $this->connection->createSchemaManager(); $result = []; foreach ($tables as $tableName) { $columns = $schemaManager->listTableColumns($tableName); foreach ($columns as $column) { $columnName = $column->getName(); - if (!array_key_exists($columnName, self::COLUMN_NAMES)) { + if (!\array_key_exists($columnName, self::COLUMN_NAMES)) { continue; } $default = self::COLUMN_NAMES[$columnName]['default']; @@ -162,26 +166,23 @@ private function fetchNonDefaultZeroColumns(): array * Obtain the names of table columns. * * @return list + * @throws Exception */ private function fetchTableNames(): array { - return array_map( - function (Table $table): string { - return $table->getName(); - }, - array_filter( - $this - ->connection - ->getSchemaManager() - ->listTables(), - function (Table $table): bool { - return 'mm_' === substr($table->getName(), 0, 3); - } + return \array_values( + \array_map( + static fn (Table $table): string => $table->getName(), + \array_filter( + $this->connection->createSchemaManager()->listTables(), + static fn (Table $table): bool => \str_starts_with($table->getName(), 'mm_') + ) ) ); } // @codingStandardsIgnoreStart + /** * Fix a table column. * @@ -190,16 +191,17 @@ function (Table $table): bool { * @param TColumnInformation $information The column information. * * @return void + * @throws Exception */ private function fixColumn(string $tableName, string $columnName, array $information): void { $this->connection->executeQuery( - sprintf( + \sprintf( 'ALTER TABLE `%1$s` CHANGE COLUMN `%2$s` `%2$s` %3$s NOT NULL DEFAULT %4$s', $tableName, $columnName, $information['type'], - var_export($information['default'], true), + \var_export($information['default'], true), ) ); } diff --git a/src/CoreBundle/Migration/TableCollationMigration.php b/src/CoreBundle/Migration/TableCollationMigration.php index 9f126adc6..09305be63 100644 --- a/src/CoreBundle/Migration/TableCollationMigration.php +++ b/src/CoreBundle/Migration/TableCollationMigration.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 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. @@ -13,7 +13,7 @@ * @package MetaModels/core * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +25,7 @@ use Contao\CoreBundle\Migration\AbstractMigration; use Contao\CoreBundle\Migration\MigrationResult; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; /** * This migration changes collation of all mm_* databases to the collation from the default table options @@ -37,14 +38,14 @@ class TableCollationMigration extends AbstractMigration * * @var Connection */ - private $connection; + private Connection $connection; /** * The default table options. * * @var array */ - private $defaultTableOptions; + private array $defaultTableOptions; /** * Create a new instance. @@ -67,7 +68,7 @@ public function getName(): string { return \sprintf( 'Change collation to %1$s and/or DB engine to %2$s of all mm_* tables.', - $this->defaultTableOptions['collate'], + $this->defaultTableOptions['collation'], $this->defaultTableOptions['engine'] ); } @@ -76,9 +77,10 @@ public function getName(): string * Must only run if: * - the mm_* tables are present AND * - there collation is not utf8mb4_unicode_ci OR - * - these engine is not InnoDB. + * - the engine is not InnoDB. * * @return bool + * @throws Exception */ public function shouldRun(): bool { @@ -94,6 +96,7 @@ public function shouldRun(): bool * Collect the tables to be updated and update them. * * @return MigrationResult + * @throws Exception */ public function run(): MigrationResult { @@ -104,33 +107,38 @@ public function run(): MigrationResult $message[] = $table; } - return new MigrationResult(true, 'Adjusted table(s): ' . implode(', ', $message)); + return new MigrationResult(true, 'Adjusted table(s): ' . \implode(', ', $message)); } /** * Fetch all tables that are not right collection or DB engine yet. * * @return array + * @throws Exception */ private function fetchPendingTables(): array { - $schemaManager = $this->connection->getSchemaManager(); + $schemaManager = $this->connection->createSchemaManager(); $tableNames = $schemaManager->listTableNames(); $results = []; foreach ($tableNames as $tableName) { // Only MM model tables. - if ('mm_' !== substr($tableName, 0, 3)) { + if (!\str_starts_with($tableName, 'mm_')) { continue; } // Retrieve table data. $result = $this->connection ->executeQuery(sprintf('SHOW TABLE STATUS LIKE \'%1$s\'', $tableName)) - ->fetch(); + ->fetchAssociative(); + if (false === $result) { + continue; + } // Check collation and DB engine and collect tables with false data. - if (($this->defaultTableOptions['collate'] !== $result['Collation']) + if ( + ($this->defaultTableOptions['collation'] !== $result['Collation']) || ($this->defaultTableOptions['engine'] !== $result['Engine']) ) { $results[] = $tableName; @@ -146,11 +154,12 @@ private function fetchPendingTables(): array * @param string $tableName The name of the table. * * @return void + * @throws Exception */ private function fixTable(string $tableName): void { $this->connection->executeQuery( - sprintf( + \sprintf( 'ALTER TABLE %1$s ENGINE=%2$s DEFAULT CHARSET=%3$s COLLATE %4$s @@ -158,7 +167,7 @@ private function fixTable(string $tableName): void $tableName, $this->defaultTableOptions['engine'], $this->defaultTableOptions['charset'], - $this->defaultTableOptions['collate'] + $this->defaultTableOptions['collation'] ) ); } diff --git a/src/CoreBundle/Resources/config/content-elements.yml b/src/CoreBundle/Resources/config/content-elements.yml index b8e814e58..ba6262b05 100644 --- a/src/CoreBundle/Resources/config/content-elements.yml +++ b/src/CoreBundle/Resources/config/content-elements.yml @@ -9,3 +9,6 @@ services: - '@metamodels.render_setting_factory' - '@event_dispatcher' - '@MetaModels\Filter\FilterUrlBuilder' + - "@contao.translation.translator" + - '@router' + - '@contao.routing.scope_matcher' diff --git a/src/CoreBundle/Resources/config/dc-general/environment-populator.yml b/src/CoreBundle/Resources/config/dc-general/environment-populator.yml index 41a1cf2bb..bcf4016b5 100644 --- a/src/CoreBundle/Resources/config/dc-general/environment-populator.yml +++ b/src/CoreBundle/Resources/config/dc-general/environment-populator.yml @@ -32,6 +32,7 @@ services: arguments: - "@event_dispatcher" - "@metamodels.view_combination" + - "@translator" tags: - name: kernel.event_listener event: dc-general.factory.populate-environment diff --git a/src/CoreBundle/Resources/config/dc-general/listener.yml b/src/CoreBundle/Resources/config/dc-general/listener.yml index b9a3f4dea..b8f349f08 100644 --- a/src/CoreBundle/Resources/config/dc-general/listener.yml +++ b/src/CoreBundle/Resources/config/dc-general/listener.yml @@ -48,3 +48,12 @@ services: - name: kernel.event_listener event: dc-general.view.contao2backend.get-paste-root-button method: handleRoot + + MetaModels\CoreBundle\EventListener\DcGeneral\Table\DeleteOperationButtonListener: + arguments: + - "@cca.dc-general.scope-matcher" + - "@translator" + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.get-operation-button + method: handle diff --git a/src/CoreBundle/Resources/config/dc-general/table/tl_attribute.yml b/src/CoreBundle/Resources/config/dc-general/table/tl_attribute.yml index 7cfc0909e..6c89298ca 100644 --- a/src/CoreBundle/Resources/config/dc-general/table/tl_attribute.yml +++ b/src/CoreBundle/Resources/config/dc-general/table/tl_attribute.yml @@ -40,22 +40,6 @@ services: event: dc-general.model.pre-edit-model method: handle - metamodels.listener.table.tl_metamodel_attribute.attribute_saved_listener: - class: MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute\AttributeSavedListener - parent: metamodels.listener.table.tl_metamodel_attribute.listener_base - tags: - - name: kernel.event_listener - event: dc-general.model.post-persist - method: handle - - metamodels.listener.table.tl_metamodel_attribute.attribute_deleted_listener: - class: MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute\AttributeDeletedListener - parent: metamodels.listener.table.tl_metamodel_attribute.listener_base - tags: - - name: kernel.event_listener - event: dc-general.model.pre-delete - method: handle - metamodels.dc_general.table.tl_metamodel_attribute.type_renderer: class: MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute\AttributeRendererListener parent: metamodels.listener.table.tl_metamodel_attribute.listener_base @@ -80,3 +64,19 @@ services: - name: kernel.event_listener event: dc-general.view.contao2backend.encode-property-value-from-widget method: encodeValue + + MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute\GetVariantListener: + parent: metamodels.listener.table.tl_metamodel_attribute.listener_base + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.build-widget + method: buildWidget + + MetaModels\CoreBundle\EventListener\DcGeneral\Table\Attribute\AttributeSchemaManagerHintListener: + parent: metamodels.listener.table.tl_metamodel_attribute.listener_base + arguments: + - "@translator" + tags: + - name: kernel.event_listener + event: dc-general.model.pre-edit-model + method: handle diff --git a/src/CoreBundle/Resources/config/dc-general/table/tl_dca.yml b/src/CoreBundle/Resources/config/dc-general/table/tl_dca.yml index dc148e225..bb5a08dce 100644 --- a/src/CoreBundle/Resources/config/dc-general/table/tl_dca.yml +++ b/src/CoreBundle/Resources/config/dc-general/table/tl_dca.yml @@ -1,6 +1,10 @@ services: metamodels.dc_general.table.tl_metamodel_dca.backend_section_options: class: MetaModels\CoreBundle\EventListener\DcGeneral\Table\Dca\BackendSectionOptionListener + arguments: + - '@contao.menu.backend_builder' + - '@translator' + - '@contao.framework' tags: - name: kernel.event_listener event: dc-general.view.contao2backend.get-property-options @@ -36,3 +40,11 @@ services: - name: kernel.event_listener event: dc-general.view.contao2backend.get-property-options method: handle + + MetaModels\CoreBundle\EventListener\DcGeneral\Table\Dca\RenderModeHintListener: + arguments: + - "@translator" + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.build-widget + method: handle diff --git a/src/CoreBundle/Resources/config/dc-general/table/tl_dca_sortgroup.yml b/src/CoreBundle/Resources/config/dc-general/table/tl_dca_sortgroup.yml index a5f236416..d0ad4fb64 100644 --- a/src/CoreBundle/Resources/config/dc-general/table/tl_dca_sortgroup.yml +++ b/src/CoreBundle/Resources/config/dc-general/table/tl_dca_sortgroup.yml @@ -6,6 +6,7 @@ services: - '@MetaModels\IFactory' - '@database_connection' - '@MetaModels\CoreBundle\Formatter\SelectAttributeOptionLabelFormatter' + - '@MetaModels\CoreBundle\Sorter\AttributeSorter' tags: - name: kernel.event_listener event: dc-general.view.contao2backend.get-property-options @@ -29,3 +30,11 @@ services: - name: kernel.event_listener event: dc-general.factory.build-data-definition method: handle + + MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSortGroup\SortGroupCreateListener: + arguments: + - '@database_connection' + tags: + - name: kernel.event_listener + event: dc-general.model.pre-edit-model + method: handle diff --git a/src/CoreBundle/Resources/config/dc-general/table/tl_dcasetting.yml b/src/CoreBundle/Resources/config/dc-general/table/tl_dcasetting.yml index 1bb8062c6..c31924af2 100644 --- a/src/CoreBundle/Resources/config/dc-general/table/tl_dcasetting.yml +++ b/src/CoreBundle/Resources/config/dc-general/table/tl_dcasetting.yml @@ -115,3 +115,29 @@ services: event: dc-general.view.contao2backend.get-operation-button method: handle priority: 2048 + + MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting\EditMaskSubHeadlineListener: + arguments: + - "@metamodels.view_combination.input_screen_information_builder" + - "@contao.string.simple_token_parser" + - "@translator" + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.get-edit-mask-subheadline + + MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting\TemplateOptionListener: + arguments: + - "@cca.dc-general.scope-matcher" + - "@metamodels.template_list" + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.get-property-options + method: handle + + MetaModels\CoreBundle\EventListener\DcGeneral\Table\DcaSetting\ManipulateWidgetListener: + arguments: + - "@cca.dc-general.scope-matcher" + tags: + - name: kernel.event_listener + event: dc-general.view.contao2backend.manipulate-widget + method: handle diff --git a/src/CoreBundle/Resources/config/dc-general/table/tl_metamodel.yml b/src/CoreBundle/Resources/config/dc-general/table/tl_metamodel.yml index 862099589..c4ef56b07 100644 --- a/src/CoreBundle/Resources/config/dc-general/table/tl_metamodel.yml +++ b/src/CoreBundle/Resources/config/dc-general/table/tl_metamodel.yml @@ -41,19 +41,17 @@ services: arguments: - "@cca.dc-general.scope-matcher" - "@metamodels.table_manipulator" + - "@metamodels.factory" tags: - name: kernel.event_listener event: dc-general.view.contao2backend.encode-property-value-from-widget method: handle - metamodels.listener.table.tl_metamodel.table_updater: - class: MetaModels\CoreBundle\EventListener\DcGeneral\Table\MetaModel\TableUpdatingListener + + MetaModels\CoreBundle\EventListener\DcGeneral\Table\MetaModel\ModelSchemaManagerHintListener: arguments: - "@cca.dc-general.scope-matcher" - - "@metamodels.table_manipulator" + - "@translator" tags: - name: kernel.event_listener - event: dc-general.model.pre-delete - method: handleDelete - - name: kernel.event_listener - event: dc-general.model.post-persist - method: handleUpdate + event: dc-general.model.pre-edit-model + method: handle diff --git a/src/CoreBundle/Resources/config/hooks.yml b/src/CoreBundle/Resources/config/hooks.yml index e0364f3ab..684bc41e2 100644 --- a/src/CoreBundle/Resources/config/hooks.yml +++ b/src/CoreBundle/Resources/config/hooks.yml @@ -19,6 +19,7 @@ services: - '@database_connection' - '@metamodels.template_list' - '@request_stack' + - '@translator' public: true MetaModels\CoreBundle\Contao\Hooks\ModuleCallback: @@ -30,6 +31,7 @@ services: - '@database_connection' - '@metamodels.template_list' - '@request_stack' + - '@translator' public: true MetaModels\CoreBundle\EventListener\GetSearchablePagesListener: @@ -39,6 +41,8 @@ services: - '@event_dispatcher' - '@metamodels.filter_setting_factory' - '@metamodels.render_setting_factory' + tags: + - { name: 'kernel.event_listener', event: 'contao.sitemap' } MetaModels\CoreBundle\EventListener\InsertTagsListener: arguments: @@ -46,6 +50,9 @@ services: - '@metamodels.factory' - '@metamodels.render_setting_factory' - '@metamodels.filter_setting_factory' + - '@?logger' + tags: + - { name: contao.hook, hook: replaceInsertTags } MetaModels\CoreBundle\Contao\Hooks\FixupUserGroupModules: arguments: diff --git a/src/CoreBundle/Resources/config/insert-tags.yml b/src/CoreBundle/Resources/config/insert-tags.yml index cbeedbad7..785599cad 100644 --- a/src/CoreBundle/Resources/config/insert-tags.yml +++ b/src/CoreBundle/Resources/config/insert-tags.yml @@ -5,8 +5,8 @@ services: MetaModels\CoreBundle\Contao\InsertTag\ReplaceParam: public: false arguments: - - '@metamodels.contao_input' - - '@metamodels.contao_session' + - '@contao.framework' + - '@request_stack' MetaModels\CoreBundle\Contao\InsertTag\ResolveLanguageTag: public: false diff --git a/src/CoreBundle/Resources/config/listeners.yml b/src/CoreBundle/Resources/config/listeners.yml index 1c9322314..d5959e8e7 100644 --- a/src/CoreBundle/Resources/config/listeners.yml +++ b/src/CoreBundle/Resources/config/listeners.yml @@ -74,9 +74,6 @@ services: event: dc-general.model.post-persist method: checkPurge - - - MetaModels\CoreBundle\EventListener\BackendNavigationListener: arguments: - '@contao.translation.translator' @@ -84,21 +81,16 @@ services: - '@metamodels.view_combination' - '@security.token_storage' - '@router' + - '@session' tags: - { name: kernel.event_listener } - - - - - - - - - - - - + MetaModels\CoreBundle\EventListener\DoctrineSchemaListener: + arguments: + $generator: '@MetaModels\Schema\SchemaGenerator' + $collector: '@MetaModels\InformationProvider\MetaModelInformationCollector' + tags: + - { name: doctrine.event_listener, event: postGenerateSchema } metamodels.backend.auth.listener: class: MetaModels\CoreBundle\EventListener\UserListener @@ -112,8 +104,6 @@ services: event: kernel.request priority: -500 - - # metamodels.listener.table.tl_metamodel_rendersettings: # class: MetaModels\DcGeneral\Events\Table\RenderSettings\Subscriber # arguments: diff --git a/src/CoreBundle/Resources/config/modules.yml b/src/CoreBundle/Resources/config/modules.yml index 2f7d91cd1..5d604d18a 100644 --- a/src/CoreBundle/Resources/config/modules.yml +++ b/src/CoreBundle/Resources/config/modules.yml @@ -9,3 +9,6 @@ services: - '@metamodels.render_setting_factory' - '@event_dispatcher' - '@MetaModels\Filter\FilterUrlBuilder' + - "@contao.translation.translator" + - '@router' + - '@contao.routing.scope_matcher' diff --git a/src/CoreBundle/Resources/config/routing.yml b/src/CoreBundle/Resources/config/routing.yml index 296521506..e786c10b8 100644 --- a/src/CoreBundle/Resources/config/routing.yml +++ b/src/CoreBundle/Resources/config/routing.yml @@ -9,3 +9,19 @@ metamodels.support_screen: metamodels.rendersetting.add_all: path: /contao/metamodels/rendersetting/add-all/{metaModel}/{renderSetting} defaults: { _controller: metamodels.controller.rendersetting.add_all, _scope: backend, _token_check: true } + +metamodels.configuration: + path: /contao/metamodels + defaults: + _controller: MetaModels\CoreBundle\Controller\Backend\ConfigurationController + _scope: backend + _dcg_referer_update: true + _token_check: true + +metamodels.metamodel: + path: /contao/metamodel/{tableName} + defaults: + _controller: MetaModels\CoreBundle\Controller\Backend\MetaModelController + _scope: backend + _dcg_referer_update: true + _token_check: true diff --git a/src/CoreBundle/Resources/config/services.yml b/src/CoreBundle/Resources/config/services.yml index 24baa37c7..25ca2647c 100644 --- a/src/CoreBundle/Resources/config/services.yml +++ b/src/CoreBundle/Resources/config/services.yml @@ -1,4 +1,10 @@ +parameters: + metamodels.managed-schema-type-names: [] services: + _defaults: + bind: + $translator: '@cca.translator.contao_translator' + MetaModels\IFactory: '@metamodels.factory' MetaModels\Filter\Setting\IFilterSettingFactory: '@metamodels.filter_setting_factory' MetaModels\Render\Setting\IRenderSettingFactory: '@metamodels.render_setting_factory' @@ -16,9 +22,18 @@ services: - "%contao.web_dir%" metamodels.cache: - class: Doctrine\Common\Cache\FilesystemCache + class: Doctrine\Common\Cache\Psr6\DoctrineProvider + factory: ['Doctrine\Common\Cache\Psr6\DoctrineProvider', 'wrap'] arguments: - - "%metamodels.cache_dir%" + - "@metamodels.cache_internal" + public: true + + metamodels.cache_internal: + class: Symfony\Component\Cache\Adapter\FilesystemAdapter + arguments: + $namespace: '' + $defaultLifetime: 0 + $directory: "%metamodels.cache_dir%" public: true metamodels.cache.purger: @@ -63,6 +78,7 @@ services: - "@database_connection" - "%contao.resources_paths%" - "%kernel.project_dir%" + - "@translator" metamodels.contao_input: class: Contao\Input @@ -95,11 +111,12 @@ services: MetaModels\Filter\FilterUrlBuilder: arguments: - - '@contao.routing.url_generator' + - '@contao.routing.page_url_generator' - '@request_stack' - '%contao.prepend_locale%' - '%contao.url_suffix%' - "@=service('contao.framework').getAdapter('Contao\\\\PageModel')" + - '%contao.legacy_routing%' # Please inject the service "MetaModels\Filter\FilterUrlBuilder" directly. metamodels.filter_url: @@ -130,6 +147,14 @@ services: parent: metamodels.controller.abstract.add_all tags: ['controller.service_arguments'] + MetaModels\CoreBundle\Controller\Backend\ConfigurationController: + tags: ['controller.service_arguments'] + + MetaModels\CoreBundle\Controller\Backend\MetaModelController: + arguments: + - '@contao.menu.backend_builder' + tags: ['controller.service_arguments'] + metamodels.controller.support_screen: class: MetaModels\CoreBundle\Controller\Backend\SupportMetaModelsController arguments: @@ -161,7 +186,7 @@ services: MetaModels\MetaModelsServiceContainer: deprecated: 'The service "%service_id%" is deprecated and to be removed in MetaModels 3 - inject needed services directly.' - configurator: 'MetaModels\CoreBundle\LegacyCompat\ServiceContainerInitializer:configure' + configurator: ['@MetaModels\CoreBundle\LegacyCompat\ServiceContainerInitializer', 'configure'] public: true MetaModels\CoreBundle\LegacyCompat\ServiceContainerInitializer: @@ -173,6 +198,65 @@ services: arguments: - '@contao.framework' + MetaModels\InformationProvider\ContaoDatabaseBackedInformationProvider: + arguments: + - '@database_connection' + + MetaModels\InformationProvider\MetaModelInformationCollector: + arguments: + - ['@MetaModels\InformationProvider\ContaoDatabaseBackedInformationProvider'] + + MetaModels\Schema\SchemaGenerator: + arguments: + - [] + public: true + + MetaModels\Schema\SchemaManager: + arguments: + - [] + public: true + + MetaModels\Schema\LegacySchemaGenerator: + arguments: + - '@metamodels.factory' + - '%metamodels.managed-schema-type-names%' + tags: + - { name: 'metamodels.schema-generator' } + + MetaModels\Schema\LegacySchemaManager: + tags: + - { name: 'metamodels.schema-manager', priority: -100 } + + MetaModels\Schema\Doctrine\DoctrineSchemaGenerator: + arguments: + - [] + tags: + - { name: 'metamodels.schema-generator' } + + MetaModels\Schema\Doctrine\DoctrineSchemaManager: + arguments: + - '@MetaModels\Schema\Doctrine\DoctrineSchemaManipulator' + tags: + - { name: 'metamodels.schema-manager' } + + MetaModels\Schema\Doctrine\DoctrineSchemaManipulator: + arguments: + - '@database_connection' + tags: + - { name: 'metamodels.schema-manipulator' } + + MetaModels\CoreBundle\Command\SchemaValidatorCommand: + arguments: + - '@MetaModels\InformationProvider\MetaModelInformationCollector' + - '@MetaModels\Schema\SchemaGenerator' + - '@MetaModels\Schema\SchemaManager' + tags: + - { name: 'console.command' } + + MetaModels\Schema\Doctrine\SystemColumnSchemaGenerator: + tags: + - { name: 'metamodels.schema-generator.doctrine' } + MetaModels\CoreBundle\Migration\TableCollationMigration: arguments: $connection: '@database_connection' @@ -202,6 +286,9 @@ services: MetaModels\CoreBundle\Formatter\SelectAttributeOptionLabelFormatter: public: false + MetaModels\CoreBundle\Sorter\AttributeSorter: + public: false + MetaModels\Helper\ToolboxFile: public: false arguments: @@ -209,3 +296,22 @@ services: - "%kernel.project_dir%" - "@contao.assets.files_context" - "@contao.image.picture_factory" + - "@session" + + MetaModels\CoreBundle\Translator\MetaModelTranslatorConfigurator: + arguments: + $factory: '@metamodels.factory' + $cache: '@metamodels.cache_internal' + + MetaModels\CoreBundle\Translator\Translator: + decorates: 'contao.translation.translator' + arguments: + - '@MetaModels\CoreBundle\Translator\Translator.inner' + + MetaModels\CoreBundle\Translator\MetaModelTranslationLoader: + arguments: + $baseTranslator: '@contao.translation.translator' + $factory: '@metamodels.factory' + $viewCombination: '@metamodels.view_combination' + $builder: '@metamodels.view_combination.input_screen_information_builder' + tags: [ { name: translation.loader, alias: 'metamodels' } ] diff --git a/src/CoreBundle/Resources/contao/config/config.php b/src/CoreBundle/Resources/contao/config/config.php index 5b95638f7..53f711b94 100644 --- a/src/CoreBundle/Resources/contao/config/config.php +++ b/src/CoreBundle/Resources/contao/config/config.php @@ -26,46 +26,10 @@ * @filesource */ -// Preserve values by extensions but insert as first entry after 'system'. -$arrOld = isset($GLOBALS['BE_MOD']['metamodels']) ? $GLOBALS['BE_MOD']['metamodels'] : array(); -unset($GLOBALS['BE_MOD']['metamodels']); -array_insert( - $GLOBALS['BE_MOD'], - (array_search('accounts', array_keys($GLOBALS['BE_MOD'])) + 1), - array - ( - 'metamodels' => array_replace_recursive( - array - ( - 'metamodels' => array - ( - 'tables' => array - ( - 'tl_metamodel', - 'tl_metamodel_attribute', - 'tl_metamodel_filter', - 'tl_metamodel_filtersetting', - 'tl_metamodel_rendersettings', - 'tl_metamodel_rendersetting', - 'tl_metamodel_dca_sortgroup', - 'tl_metamodel_dca', - 'tl_metamodel_dcasetting', - 'tl_metamodel_dca_combine', - 'tl_metamodel_dcasetting_condition', - 'tl_metamodel_searchable_pages' - ), - 'icon' => 'bundles/metamodelscore/images/backend/logo.png', - 'callback' => 'MetaModels\BackendIntegration\Module' - ) - ), - // Append all previous data here. - $arrOld - ) - ) -); +$container = \Contao\System::getContainer(); // @deprecated Use the config parameter metamodels.system_columns instead. -$GLOBALS['METAMODELS_SYSTEM_COLUMNS'] = \Contao\System::getContainer()->getParameter('metamodels.system_columns'); +$GLOBALS['METAMODELS_SYSTEM_COLUMNS'] = $container->getParameter('metamodels.system_columns'); // Front-end modules. $GLOBALS['FE_MOD']['metamodels']['metamodels_frontendfilter'] = 'MetaModels\FrontendIntegration\Module\Filter'; @@ -86,10 +50,10 @@ array('MetaModels\FrontendIntegration\FrontendFilter', 'generateClearAll'); // Add cache only if dir defined in container (and therefore we are using the cache). -if ($cacheDir = \Contao\System::getContainer()->getParameter('metamodels.cache_dir')) { +if ($cacheDir = $container->getParameter('metamodels.cache_dir')) { // We need to translate the cache dir - otherwise the backend view is distorted. See \Contao\PurgeData::run(). $GLOBALS['TL_PURGE']['folders']['metamodels']['affected'] = [str_replace( - \Contao\System::getContainer()->getParameter('kernel.cache_dir') . '/', + $container->getParameter('kernel.cache_dir') . '/', '%s/', $cacheDir )]; @@ -114,7 +78,7 @@ $GLOBALS['METAMODELS']['metainformation']['allowedDescription'][] = 'translatedlongtext'; $GLOBALS['METAMODELS']['metainformation']['allowedDescription'][] = 'combinedvalues'; -array_insert($GLOBALS['BE_FFL'], 15, array +\Contao\ArrayUtil::arrayInsert($GLOBALS['BE_FFL'], 15, array ( 'mm_subdca' => 'MetaModels\Widgets\SubDcaWidget' )); diff --git a/src/CoreBundle/Resources/contao/config/services.php b/src/CoreBundle/Resources/contao/config/services.php index 10940c67f..1ed87e5c2 100644 --- a/src/CoreBundle/Resources/contao/config/services.php +++ b/src/CoreBundle/Resources/contao/config/services.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Sven Baumann * @author Oliver Willmes * @author Richard Henkenjohann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ diff --git a/src/CoreBundle/Resources/contao/dca/tl_content.php b/src/CoreBundle/Resources/contao/dca/tl_content.php old mode 100644 new mode 100755 index d452d8a3b..a02e260b4 --- a/src/CoreBundle/Resources/contao/dca/tl_content.php +++ b/src/CoreBundle/Resources/contao/dca/tl_content.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,7 +18,7 @@ * @author Ingolf Steinhardt * @author Richard Henkenjohann * @author Sven Baumann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -47,7 +47,7 @@ '{type_legend},type,headline;' . '{mm_filter_legend},metamodel,metamodel_filtering,metamodel_fef_template,metamodel_fef_params,' . 'metamodel_fef_autosubmit,metamodel_fef_hideclearfilter,metamodel_available_values,' . - 'metamodel_jumpTo,metamodel_fef_urlfragment;' . + 'metamodel_jumpTo,metamodel_fef_id,metamodel_fef_urlfragment;' . '{protected_legend:hide},protected;' . '{expert_legend:hide},guests,cssID,space;' . '{invisible_legend:hide},invisible,start,stop'; @@ -64,18 +64,18 @@ $GLOBALS['TL_DCA']['tl_content']['palettes']['__selector__'][] = 'metamodel_use_parameters'; // Insert new Subpalettes after position 1. -array_insert( +\Contao\ArrayUtil::arrayInsert( $GLOBALS['TL_DCA']['tl_content']['subpalettes'], 1, [ 'metamodel_use_limit' => 'metamodel_offset,metamodel_limit', - 'metamodel_sort_override' => 'metamodel_sort_param_type,metamodel_order_by_param,metamodel_order_dir_param', + 'metamodel_sort_override' => 'metamodel_sort_param_type,metamodel_order_by_param,metamodel_order_dir_param,metamodel_sort_urlfragment', 'metamodel_use_parameters' => 'metamodel_parameters' ] ); // Fields. -array_insert( +\Contao\ArrayUtil::arrayInsert( $GLOBALS['TL_DCA']['tl_content']['fields'], 1, [ @@ -266,6 +266,16 @@ ], 'sql' => "varchar(64) NOT NULL default ''" ], + 'metamodel_sort_urlfragment' => [ + 'label' => &$GLOBALS['TL_LANG']['tl_content']['metamodel_sort_urlfragment'], + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ + 'tl_class' => 'clr w50', + 'rgxp' => 'alias' + ], + 'sql' => "char(255) NOT NULL default ''" + ], 'metamodel_filtering' => [ 'label' => &$GLOBALS['TL_LANG']['tl_content']['metamodel_filtering'], @@ -345,12 +355,22 @@ ], 'sql' => "int(10) unsigned NOT NULL default '0'" ], + 'metamodel_fef_id' => [ + 'label' => &$GLOBALS['TL_LANG']['tl_content']['metamodel_fef_id'], + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ + 'tl_class' => 'w50', + 'rgxp' => 'alias' + ], + 'sql' => "char(255) NOT NULL default ''" + ], 'metamodel_fef_urlfragment' => [ 'label' => &$GLOBALS['TL_LANG']['tl_content']['metamodel_fef_urlfragment'], 'exclude' => true, 'inputType' => 'text', 'eval' => [ - 'tl_class' => 'w50', + 'tl_class' => 'clr w50', 'rgxp' => 'alias' ], 'sql' => "char(255) NOT NULL default ''" diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel.php index 6f52f702f..5a1b0921f 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,13 +21,11 @@ * @author Richard Henkenjohann * @author Ingolf Steinhardt * @author Cliff Parnitzky - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ -$this->loadLanguageFile('languages'); - $GLOBALS['TL_DCA']['tl_metamodel'] = [ 'config' => [ 'dataContainer' => 'General', @@ -36,7 +34,7 @@ 'sql' => [ 'keys' => [ 'id' => 'primary', - 'tableName' => 'index', + 'tableName' => 'unique', ], ], ], @@ -316,72 +314,80 @@ ], 'global_operations' => [ 'all' => [ - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ], ], 'operations' => [ 'edit' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['editheader'], - 'href' => 'act=edit', - 'icon' => 'edit.svg', + 'label' => 'editheader.label', + 'description' => 'editheader.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg', ], 'cut' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['cut'], - 'href' => 'act=paste&mode=cut', - 'icon' => 'cut.svg' + 'label' => 'cut.label', + 'description' => 'cut.description', + 'href' => 'act=paste&mode=cut', + 'icon' => 'cut.svg' ], 'delete' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', ], 'show' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' ], 'fields' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['fields'], - 'href' => 'table=tl_metamodel_attribute', - 'icon' => 'bundles/metamodelscore/images/icons/fields.png', - 'idparam' => 'pid' + 'label' => 'fields.label', + 'description' => 'fields.description', + 'href' => 'table=tl_metamodel_attribute', + 'icon' => 'bundles/metamodelscore/images/icons/fields.png', + 'idparam' => 'pid' ], 'rendersettings' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['rendersettings'], - 'href' => 'table=tl_metamodel_rendersettings', - 'icon' => 'bundles/metamodelscore/images/icons/rendersettings.png', - 'idparam' => 'pid' + 'label' => 'rendersettings.label', + 'description' => 'rendersettings.description', + 'href' => 'table=tl_metamodel_rendersettings', + 'icon' => 'bundles/metamodelscore/images/icons/rendersettings.png', + 'idparam' => 'pid' ], 'dca' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['dca'], - 'href' => 'table=tl_metamodel_dca', - 'icon' => 'bundles/metamodelscore/images/icons/dca.png', - 'idparam' => 'pid' + 'label' => 'dca.label', + 'description' => 'dca.description', + 'href' => 'table=tl_metamodel_dca', + 'icon' => 'bundles/metamodelscore/images/icons/dca.png', + 'idparam' => 'pid' ], 'searchable_pages' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['searchable_pages'], - 'href' => 'table=tl_metamodel_searchable_pages', - 'icon' => 'bundles/metamodelscore/images/icons/searchable_pages.png', - 'idparam' => 'pid' + 'label' => 'searchable_pages.label', + 'description' => 'searchable_pages.description', + 'href' => 'table=tl_metamodel_searchable_pages', + 'icon' => 'bundles/metamodelscore/images/icons/searchable_pages.png', + 'idparam' => 'pid' ], 'filter' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['filter'], - 'href' => 'table=tl_metamodel_filter', - 'icon' => 'bundles/metamodelscore/images/icons/filter.png', - 'idparam' => 'pid' + 'label' => 'filter.label', + 'description' => 'filter.description', + 'href' => 'table=tl_metamodel_filter', + 'icon' => 'bundles/metamodelscore/images/icons/filter.png', + 'idparam' => 'pid' ], 'dca_combine' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['dca_combine'], - 'href' => 'table=tl_metamodel_dca_combine&act=edit', - 'icon' => 'bundles/metamodelscore/images/icons/dca_combine.png', - 'idparam' => 'pid' + 'label' => 'dca_combine.label', + 'description' => 'dca_combine.description', + 'href' => 'table=tl_metamodel_dca_combine&act=edit', + 'icon' => 'bundles/metamodelscore/images/icons/dca_combine.png', + 'idparam' => 'pid' ], ] ], @@ -408,41 +414,47 @@ ], ], 'fields' => [ - 'id' => - [ - 'sql' => 'int(10) unsigned NOT NULL auto_increment' - ], - 'tstamp' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'id' => [ + 'label' => 'id.label', + 'description' => 'id.description', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' ], - 'sorting' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['sorting'], - 'sorting' => true, - 'flag' => 11, - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'tstamp' => [ + 'label' => 'tstamp.label', + 'description' => 'tstamp.description', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'name' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['name'], - 'sorting' => true, - 'flag' => 3, - 'length' => 1, - 'exclude' => true, - 'inputType' => 'text', - 'search' => true, - 'eval' => [ + 'sorting' => [ + 'label' => 'sorting.label', + 'description' => 'sorting.description', + 'sorting' => true, + 'flag' => 11, + 'sql' => "int(10) unsigned NOT NULL default '0'" + ], + 'name' => [ + 'label' => 'name.label', + 'description' => 'name.description', + 'sorting' => true, + 'flag' => 3, + 'length' => 1, + 'exclude' => true, + 'inputType' => 'text', + 'search' => true, + 'eval' => [ 'mandatory' => true, 'maxlength' => 255, 'tl_class' => 'w50' ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], - 'tableName' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['tableName'], - 'sorting' => true, - 'exclude' => true, - 'inputType' => 'text', - 'search' => true, - 'eval' => [ + 'tableName' => [ + 'label' => 'tableName.label', + 'description' => 'tableName.description', + 'sorting' => true, + 'exclude' => true, + 'inputType' => 'text', + 'search' => true, + 'eval' => [ 'mandatory' => true, 'maxlength' => 64, 'doNotCopy' => true, @@ -450,68 +462,72 @@ // Hide at overrideAll. 'doNotOverrideMultiple' => true ], - 'sql' => "varchar(64) NOT NULL default ''" - ], - 'mode' => [ - 'sql' => "int(1) unsigned NOT NULL default '1'" + 'sql' => "varchar(64) NOT NULL default ''" ], - 'translated' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['translated'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'translated' => [ + 'label' => 'translated.label', + 'description' => 'translated.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'clr w50 cbx m12', 'submitOnChange' => true ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], - 'languages' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['languages'], - 'exclude' => true, - 'inputType' => 'multiColumnWizard', - 'eval' => + 'languages' => [ + 'label' => 'languages.label', + 'description' => 'languages.description', + 'exclude' => true, + 'inputType' => 'multiColumnWizard', + 'eval' => [ - 'tl_class' => 'clr w50', - 'columnFields' => [ + 'useTranslator' => true, + 'tl_class' => 'clr w50', + 'columnFields' => [ 'langcode' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['languages_langcode'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'languages_langcode.label', + 'description' => 'languages_langcode.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'style' => 'width:100%;', 'chosen' => 'true' ], ], 'isfallback' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['languages_isfallback'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'languages_isfallback.label', + 'description' => 'languages_isfallback.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'style' => 'width:100%;', ], ], ], ], - 'sql' => 'text NULL' + 'sql' => 'text NULL' ], - 'varsupport' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['varsupport'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ - 'tl_class' => 'clr w50' - ], - 'sql' => "char(1) NOT NULL default ''" + 'varsupport' => [ + 'label' => 'varsupport.label', + 'description' => 'varsupport.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ + 'tl_class' => 'clr w50' + ], + 'sql' => "char(1) NOT NULL default ''" ], 'localeterritorysupport' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel']['localeterritorysupport'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'localeterritorysupport.label', + 'description' => 'localeterritorysupport.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12', 'submitOnChange' => true ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], ], ]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_attribute.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_attribute.php index bff838993..729e8fb08 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_attribute.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_attribute.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,7 +18,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,21 +31,22 @@ 'enableVersioning' => false, 'sql' => [ 'keys' => [ - 'id' => 'primary', - 'pid' => 'index', - 'colname' => 'index' + 'id' => 'primary', + 'pid' => 'index', + 'colname' => 'index', + 'pid,colname' => 'unique', ], ], ], 'dca_config' => [ 'data_provider' => [ - 'default' => [ + 'default' => [ 'source' => 'tl_metamodel_attribute' ], - 'parent' => [ + 'parent' => [ 'source' => 'tl_metamodel' ], - 'tl_metamodel_rendersetting' => [ + 'tl_metamodel_rendersetting' => [ 'source' => 'tl_metamodel_rendersetting' ], 'tl_metamodel_dcasetting' => [ @@ -54,7 +55,7 @@ 'tl_metamodel_dcasetting_condition' => [ 'source' => 'tl_metamodel_dcasetting_condition' ], - 'tl_metamodel_dca_sortgroup' => [ + 'tl_metamodel_dca_sortgroup' => [ 'source' => 'tl_metamodel_dca_sortgroup' ] ], @@ -174,37 +175,39 @@ ], 'global_operations' => [ 'all' => [ - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ] ], 'operations' => [ 'edit' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['edit'], - 'href' => 'act=edit', - 'icon' => 'edit.svg' + 'label' => 'edit.label', + 'description' => 'edit.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg' ], 'cut' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['cut'], - 'href' => 'act=paste&mode=cut', - 'icon' => 'cut.svg', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'cut.label', + 'description' => 'cut.description', + 'href' => 'act=paste&mode=cut', + 'icon' => 'cut.svg', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ], 'delete' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', ], 'show' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' ], ] ], @@ -227,8 +230,6 @@ 'isvariant', 'isunique' ], - 'metamodeloverview' => [], - 'backenddisplay' => [], ], // Default palette for MetaModelAttributeSimple derived types. // WARNING: even though it is empty, we have to keep it as otherwise @@ -248,23 +249,28 @@ // Fields. 'fields' => [ 'id' => [ - 'sql' => 'int(10) unsigned NOT NULL auto_increment' + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' ], 'pid' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'sorting' => [ + 'label' => 'sorting.label', 'sorting' => true, 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'tstamp' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'type' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['type'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'type.label', + 'description' => 'type.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'includeBlankOption' => true, 'doNotSaveEmpty' => true, 'alwaysSave' => true, @@ -273,61 +279,65 @@ 'tl_class' => 'w50', 'chosen' => true ], - 'filter' => true, - 'search' => true, - 'sql' => "varchar(64) NOT NULL default ''" + 'filter' => true, + 'search' => true, + 'sql' => "varchar(64) NOT NULL default ''" ], 'name' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['name'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'name.label', + 'description' => 'name.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'tl_class' => 'clr' ], - 'search' => true, - 'sql' => 'text NULL' + 'search' => true, + 'sql' => 'text NULL' ], 'description' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['description'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'description.label', + 'description' => 'description.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'tl_class' => 'clr' ], - 'sql' => 'text NULL' + 'sql' => 'text NULL' ], // AVOID: doNotCopy => true, as child records won't be copied when copy metamodel. 'colname' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['colname'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'colname.label', + 'description' => 'colname.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'mandatory' => true, 'maxlength' => 64, 'tl_class' => 'w50', // Hide at overrideAll. 'doNotOverrideMultiple' => true ], - 'search' => true, - 'sql' => "varchar(64) NOT NULL default ''" + 'search' => true, + 'sql' => "varchar(64) NOT NULL default ''" ], 'isvariant' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['isvariant'], - 'inputType' => 'checkbox', - 'eval' => [ - 'submitOnChange' => true, - 'tl_class' => 'w50 cbx m12' + 'label' => 'isvariant.label', + 'description' => 'isvariant.description', + 'inputType' => 'checkbox', + 'eval' => [ + 'tl_class' => 'w50 cbx m12' ], - 'filter' => true, - 'sql' => "char(1) NOT NULL default ''" + 'filter' => true, + 'sql' => "char(1) NOT NULL default ''" ], 'isunique' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_attribute']['isunique'], - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'isunique.label', + 'description' => 'isunique.description', + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12' ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], ] ]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca.php index d5722fa5f..f8da7c337 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,7 +21,7 @@ * @author Richard Henkenjohann * @author Ingolf Steinhardt * @author Cliff Parnitzky - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -98,7 +98,6 @@ ], ], ], - [ 'from' => 'tl_metamodel_dca', 'to' => 'tl_metamodel_dcasetting', @@ -132,48 +131,52 @@ ], 'global_operations' => [ 'all' => [ - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ], ], 'operations' => [ 'edit' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['edit'], - 'href' => 'act=edit', - 'icon' => 'edit.svg', + 'label' => 'edit.label', + 'description' => 'edit.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg', ], 'copy' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['copy'], - 'href' => 'act=copy', - 'icon' => 'copy.svg', + 'label' => 'copy.label', + 'description' => 'copy.description', + 'href' => 'act=copy', + 'icon' => 'copy.svg', ], 'delete' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', ], 'show' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' ], 'groupsort_settings' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['groupsort_settings'], - 'href' => 'table=tl_metamodel_dca_sortgroup', - 'icon' => 'bundles/metamodelscore/images/icons/dca_groupsortsettings.png', - 'idparam' => 'pid' + 'label' => 'groupsort_settings.label', + 'description' => 'groupsort_settings.description', + 'href' => 'table=tl_metamodel_dca_sortgroup', + 'icon' => 'bundles/metamodelscore/images/icons/dca_groupsortsettings.png', + 'idparam' => 'pid' ], 'settings' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['settings'], - 'href' => 'table=tl_metamodel_dcasetting', - 'icon' => 'bundles/metamodelscore/images/icons/dca_setting.png', - 'idparam' => 'pid' + 'label' => 'settings.label', + 'description' => 'settings.description', + 'href' => 'table=tl_metamodel_dcasetting', + 'icon' => 'bundles/metamodelscore/images/icons/dca_setting.png', + 'idparam' => 'pid' ], ] ], @@ -190,7 +193,8 @@ 'backendcaption', ], 'display' => [ - 'rendermode' + 'rendermode', + 'subheadline' ], 'permissions' => [ 'iseditable', @@ -222,137 +226,156 @@ ], 'fields' => [ 'id' => [ - 'sql' => 'int(10) unsigned NOT NULL auto_increment' + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' ], 'pid' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'sorting' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'sorting.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'tstamp' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'name' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['name'], - 'exclude' => true, - 'search' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'name.label', + 'description' => 'name.description', + 'exclude' => true, + 'search' => true, + 'inputType' => 'text', + 'eval' => [ 'mandatory' => true, 'maxlength' => 255, 'tl_class' => 'w50' ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], 'rendertype' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendertype'], - 'inputType' => 'select', - 'default' => 'standalone', - 'reference' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendertypes'], - 'eval' => [ + 'label' => 'rendertype.label', + 'description' => 'rendertype.description', + 'inputType' => 'select', + 'default' => 'standalone', + 'reference' => [ + 'standalone' => 'rendertypes.standalone', + 'ctable' => 'rendertypes.ctable', + ], + 'eval' => [ 'tl_class' => 'w50', 'submitOnChange' => true, 'includeBlankOption' => true ], - 'sql' => "varchar(10) NOT NULL default ''" + 'sql' => "varchar(10) NOT NULL default ''" ], 'ptable' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['ptable'], - 'inputType' => 'select', - 'eval' => [ + 'label' => 'ptable.label', + 'description' => 'ptable.description', + 'inputType' => 'select', + 'eval' => [ 'tl_class' => 'w50', 'includeBlankOption' => true, 'chosen' => true ], - 'sql' => "varchar(64) NOT NULL default ''" + 'sql' => "varchar(64) NOT NULL default ''" ], 'rendermode' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendermode'], - 'inputType' => 'select', - 'default' => 'flat', - 'eval' => [ + 'label' => 'rendermode.label', + 'description' => 'rendermode.description', + 'inputType' => 'select', + 'default' => 'flat', + 'eval' => [ 'tl_class' => 'w50', 'submitOnChange' => true ], - 'sql' => "varchar(12) NOT NULL default ''" + 'sql' => "varchar(12) NOT NULL default ''" ], 'showColumns' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['showColumns'], - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'showColumns.label', + 'description' => 'showColumns.description', + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 m12 cbx' ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'backendsection' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['backendsection'], - 'exclude' => true, - 'inputType' => 'select', - 'default' => 'metamodels', - 'reference' => &$GLOBALS['TL_LANG']['MOD'], - 'eval' => + 'label' => 'backendsection.label', + 'description' => 'backendsection.description', + 'exclude' => true, + 'inputType' => 'select', + 'default' => 'metamodels', + 'eval' => [ 'includeBlankOption' => true, 'valign' => 'top', 'chosen' => true, 'tl_class' => 'w50' ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], 'backendicon' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['backendicon'], - 'exclude' => true, - 'inputType' => 'fileTree', - 'eval' => [ + 'label' => 'backendicon.label', + 'description' => 'backendicon.description', + 'exclude' => true, + 'inputType' => 'fileTree', + 'eval' => [ 'fieldType' => 'radio', 'files' => true, 'filesOnly' => true, 'extensions' => 'jpg,jpeg,gif,png,tif,tiff,svg', 'tl_class' => 'clr' ], - 'sql' => 'blob NULL' + 'sql' => 'blob NULL' ], 'backendcaption' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['backendcaption'], - 'exclude' => true, - 'inputType' => 'multiColumnWizard', - 'eval' => [ - 'tl_class' => 'clr', - 'columnFields' => [ + 'label' => 'backendcaption.label', + 'description' => 'backendcaption.description', + 'exclude' => true, + 'inputType' => 'multiColumnWizard', + 'eval' => [ + 'useTranslator' => true, + 'tl_class' => 'clr', + 'columnFields' => [ 'langcode' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['becap_langcode'], - 'exclude' => true, - 'inputType' => 'select', - 'options' => $this->getLanguages(), - 'eval' => [ - 'tl_class' => 'clr', - 'style' => 'width:100%', + 'label' => 'becap_langcode.label', + 'description' => 'becap_langcode.description', + 'exclude' => true, + 'inputType' => 'select', + 'options' => $this->getLanguages(), + 'eval' => [ + 'tl_class' => '', + 'style' => 'width:400px', 'chosen' => 'true' ] ], 'label' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['becap_label'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'becap_label.label', + 'description' => 'becap_label.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'style' => 'width:100%', ] ], 'description' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['becap_description'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'becap_description.label', + 'description' => 'becap_description.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'style' => 'width:100%', ] ], ], ], - 'sql' => 'text NULL' + 'sql' => 'text NULL' ], 'panelLayout' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['panelLayout'], + 'label' => 'panelLayout.label', + 'description' => 'panelLayout.description', 'exclude' => true, 'inputType' => 'text', 'default' => 'limit', @@ -365,31 +388,44 @@ 'sql' => 'blob NULL' ], 'iseditable' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['iseditable'], - 'inputType' => 'checkbox', - 'default' => 1, - 'eval' => [ + 'label' => 'iseditable.label', + 'description' => 'iseditable.description', + 'inputType' => 'checkbox', + 'default' => 1, + 'eval' => [ 'tl_class' => 'w50 cbx', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'iscreatable' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['iscreatable'], - 'inputType' => 'checkbox', - 'default' => 1, - 'eval' => [ + 'label' => 'iscreatable.label', + 'description' => 'iscreatable.description', + 'inputType' => 'checkbox', + 'default' => 1, + 'eval' => [ 'tl_class' => 'w50 cbx', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'isdeleteable' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca']['isdeleteable'], - 'inputType' => 'checkbox', - 'default' => 1, - 'eval' => [ + 'label' => 'isdeleteable.label', + 'description' => 'isdeleteable.description', + 'inputType' => 'checkbox', + 'default' => 1, + 'eval' => [ 'tl_class' => 'clr w50 cbx', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" + ], + 'subheadline' => [ + 'label' => 'subheadline.label', + 'description' => 'subheadline.description', + 'inputType' => 'text', + 'eval' => [ + 'maxlength' => 255, + 'tl_class' => 'w50' + ], + 'sql' => "varchar(255) NOT NULL default ''" ] ] ]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca_combine.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca_combine.php index c0d0f8c79..8af295dbd 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca_combine.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca_combine.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -39,7 +39,7 @@ ] ], 'dca_config' => [ - 'data_provider' => [ + 'data_provider' => [ 'default' => [ 'class' => 'ContaoCommunityAlliance\DcGeneral\Data\TableRowsAsRecordsDataProvider', 'source' => 'tl_metamodel_dca_combine', @@ -49,15 +49,15 @@ ], 'childCondition' => [ [ - 'from' => 'tl_metamodel', - 'to' => 'tl_metamodel_dca_combine', - 'setOn' => [ + 'from' => 'tl_metamodel', + 'to' => 'tl_metamodel_dca_combine', + 'setOn' => [ [ 'to_field' => 'pid', 'from_field' => 'id', ], ], - 'filter' => [ + 'filter' => [ [ 'local' => 'pid', 'remote' => 'id', @@ -71,28 +71,34 @@ ], ], 'palettes' => [ - 'default' => 'rows' + 'default' => '{dca_combiner_legend},rows' ], 'fields' => [ 'id' => [ - 'sql' => 'int(10) unsigned NOT NULL auto_increment' + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' ], 'pid' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'sorting' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'sorting.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'tstamp' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'rows' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['dca_combiner'], - 'exclude' => true, - 'inputType' => 'multiColumnWizard', - 'eval' => [ - 'tl_class' => 'dca_combine', - 'columnFields' => [ + 'label' => 'dca_combiner.label', + 'description' => 'dca_combiner.description', + 'exclude' => true, + 'inputType' => 'multiColumnWizard', + 'eval' => [ + 'useTranslator' => true, + 'tl_class' => 'dca_combine', + 'columnFields' => [ 'id' => [ 'label' => null, 'exclude' => true, @@ -103,10 +109,11 @@ ] ], 'fe_group' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['fe_group'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'fe_group.label', + 'description' => 'fe_group.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'includeBlankOption' => true, 'blankOptionLabel' => '*', 'style' => 'width:100%', @@ -114,10 +121,11 @@ ], ], 'be_group' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['be_group'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'be_group.label', + 'description' => 'be_group.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'includeBlankOption' => true, 'blankOptionLabel' => '*', 'style' => 'width:100%', @@ -125,21 +133,23 @@ ], ], 'view_id' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['view_id'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => + 'label' => 'view_id.label', + 'description' => 'view_id.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ - 'includeBlankOption' => true, - 'style' => 'width:100%', - 'chosen' => 'true' + 'includeBlankOption' => true, + 'style' => 'width:100%', + 'chosen' => 'true' ], ], 'dca_id' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['dca_id'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => + 'label' => 'dca_id.label', + 'description' => 'dca_id.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'includeBlankOption' => true, 'style' => 'width:100%', @@ -150,18 +160,22 @@ ], ], 'fe_group' => [ + 'label' => 'fe_group.label', // keep signed as anonymous are -1 - 'sql' => "int(10) NOT NULL default '0'" + 'sql' => "int(10) NOT NULL default '0'" ], 'be_group' => [ + 'label' => 'be_group.label', // keep signed as admins are -1 - 'sql' => "int(10) NOT NULL default '0'" + 'sql' => "int(10) NOT NULL default '0'" ], 'view_id' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'view_id.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'dca_id' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'dca_id.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ] ] ]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca_sortgroup.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca_sortgroup.php index bac414b1f..12972a234 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca_sortgroup.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_dca_sortgroup.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Richard Henkenjohann * @author Ingolf Steinhardt * @author Cliff Parnitzky - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -73,16 +73,16 @@ ] ], [ - 'from' => 'tl_metamodel', - 'to' => 'tl_metamodel_dca', - 'setOn' => + 'from' => 'tl_metamodel', + 'to' => 'tl_metamodel_dca', + 'setOn' => [ [ 'to_field' => 'pid', 'from_field' => 'id', ], ], - 'filter' => [ + 'filter' => [ [ 'local' => 'pid', 'remote' => 'id', @@ -105,37 +105,45 @@ ], 'global_operations' => [ 'all' => [ - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ], ], 'operations' => [ 'edit' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['edit'], - 'href' => 'act=edit', - 'icon' => 'edit.svg', + 'label' => 'edit.label', + 'description' => 'edit.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg', ], 'copy' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['copy'], - 'href' => 'act=copy', - 'icon' => 'copy.svg', + 'label' => 'copy.label', + 'description' => 'copy.description', + 'href' => 'act=copy', + 'icon' => 'copy.svg', ], 'delete' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', ], 'show' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' ], + 'toggle' => [ + 'label' => 'toggle.label', + 'description' => 'toggle.description', + 'icon' => 'visible.svg', + 'toggleProperty' => 'published' + ] ] ], 'metapalettes' => [ @@ -174,101 +182,130 @@ ], 'fields' => [ 'id' => [ - 'sql' => 'int(10) unsigned NOT NULL auto_increment' + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' ], 'pid' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" + ], + 'sorting' => [ + 'label' => 'sorting.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'sorting' => - [ - 'sql' => "int(10) unsigned NOT NULL default '0'" - ], 'tstamp' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" + ], + 'published' => [ + 'label' => 'published.label', + 'default' => 1, + 'sql' => "char(1) NOT NULL default '1'" ], 'name' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['name'], - 'exclude' => true, - 'search' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'name.label', + 'description' => 'name.description', + 'exclude' => true, + 'search' => true, + 'inputType' => 'text', + 'eval' => [ 'mandatory' => true, 'maxlength' => 255, 'tl_class' => 'w50' ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], 'isdefault' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['isdefault'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'isdefault.label', + 'description' => 'isdefault.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 m12 cbx', 'fallback' => true ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'ismanualsort' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['ismanualsort'], - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'ismanualsort.label', + 'description' => 'ismanualsort.description', + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx', 'submitOnChange' => true ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'rendersort' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendersort'], - 'exclude' => true, - 'inputType' => 'select', - 'options' => ['asc', 'desc'], - 'eval' => [ + 'label' => 'rendersort.label', + 'description' => 'rendersort.description', + 'exclude' => true, + 'inputType' => 'select', + 'options' => ['asc', 'desc'], + 'eval' => [ 'tl_class' => 'w50', ], - 'reference' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendersortdirections'], - 'sql' => "varchar(10) NOT NULL default 'asc'" + 'reference' => [ + 'asc' => 'rendersortdirections.asc', + 'desc' => 'rendersortdirections.desc', + ], + 'sql' => "varchar(10) NOT NULL default 'asc'" ], 'rendersortattr' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendersortattr'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'rendersortattr.label', + 'description' => 'rendersortattr.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'tl_class' => 'w50 clr', + 'chosen' => true ], - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'rendergrouptype' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptype'], - 'exclude' => true, - 'inputType' => 'select', - 'options' => ['none', 'char', 'digit', 'day', 'weekday', 'week', 'month', 'year'], - 'default' => 'none', - 'eval' => [ + 'label' => 'rendergrouptype.label', + 'description' => 'rendergrouptype.description', + 'exclude' => true, + 'inputType' => 'select', + 'options' => ['none', 'char', 'digit', 'day', 'weekday', 'week', 'month', 'year'], + 'default' => 'none', + 'eval' => [ 'tl_class' => 'w50 clr', 'submitOnChange' => true ], - 'reference' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptypes'], - 'sql' => "varchar(10) NOT NULL default 'none'" + 'reference' => [ + 'none' => 'rendergrouptypes.none', + 'char' => 'rendergrouptypes.char', + 'digit' => 'rendergrouptypes.digit', + 'day' => 'rendergrouptypes.day', + 'weekday' => 'rendergrouptypes.weekday', + 'week' => 'rendergrouptypes.week', + 'month' => 'rendergrouptypes.month', + 'year' => 'rendergrouptypes.year', + ], + 'sql' => "varchar(10) NOT NULL default 'none'" ], 'rendergroupattr' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergroupattr'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ - 'tl_class' => 'w50', - 'submitOnChange' => true + 'label' => 'rendergroupattr.label', + 'description' => 'rendergroupattr.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ + 'tl_class' => 'w50', + 'chosen' => true ], - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'rendergrouplen' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouplen'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'rendergrouplen.label', + 'description' => 'rendergrouplen.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'tl_class' => 'w50', 'rgxp' => 'digit' ], - 'sql' => "int(10) unsigned NOT NULL default '1'" + 'sql' => "int(10) unsigned NOT NULL default '1'" ] ] ]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_dcasetting.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_dcasetting.php index 49e518acb..1b3375841 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_dcasetting.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_dcasetting.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,7 +21,7 @@ * @author Cliff Parnitzky * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -40,7 +40,7 @@ ], 'dca_config' => [ 'data_provider' => [ - 'root' => [ + 'root' => [ 'source' => 'tl_metamodel_dcasetting' ], 'parent' => [ @@ -138,52 +138,57 @@ ], 'global_operations' => [ 'addall' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['addall'], - 'class' => 'header_add_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'addall.label', + 'description' => 'addall.description', + 'class' => 'header_add_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ], 'all' => [ - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ], ], 'operations' => [ 'edit' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['edit'], - 'href' => 'act=edit', - 'icon' => 'edit.svg' + 'label' => 'edit.label', + 'description' => 'edit.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg' ], 'cut' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['cut'], - 'href' => 'act=paste&mode=cut', - 'icon' => 'cut.svg' + 'label' => 'cut.label', + 'description' => 'cut.description', + 'href' => 'act=paste&mode=cut', + 'icon' => 'cut.svg' ], 'delete' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', ], 'show' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' ], 'toggle' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['toggle'], + 'label' => 'toggle.label', + 'description' => 'toggle.description', 'icon' => 'visible.svg', 'toggleProperty' => 'published', ], 'conditions' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['conditions'], - 'href' => 'table=tl_metamodel_dcasetting_condition', - 'icon' => 'bundles/metamodelscore/images/icons/dca_condition.png', - 'idparam' => 'pid' + 'label' => 'conditions.label', + 'description' => 'conditions.description', + 'href' => 'table=tl_metamodel_dcasetting_condition', + 'icon' => 'bundles/metamodelscore/images/icons/dca_condition.png', + 'idparam' => 'pid' ], ] ], @@ -225,6 +230,7 @@ // * advanced // Core fields: // * tl_class css class to use in backend. + // * be_template template for backend widget. // * mandatory mandatory. // * alwaysSave always save. // * filterable can be filtered (in backend). @@ -245,39 +251,49 @@ ], 'fields' => [ 'id' => [ - 'sql' => 'int(10) unsigned NOT NULL auto_increment' + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' ], 'pid' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'sorting' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'sorting.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'tstamp' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'published' => [ + 'label' => 'published.label', 'default' => 1, 'sql' => "char(1) NOT NULL default '1'" ], 'dcatype' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['dcatype'], - 'exclude' => true, - 'inputType' => 'select', - 'options' => ['attribute', 'legend'], - 'reference' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['dcatypes'], - 'eval' => [ + 'label' => 'dcatype.label', + 'description' => 'dcatype.description', + 'exclude' => true, + 'inputType' => 'select', + 'options' => ['attribute', 'legend'], + 'reference' => [ + 'legend' => 'dcatypes.legend', + 'attribute' => 'dcatypes.attribute', + ], + 'eval' => [ 'tl_class' => 'w50', 'includeBlankOption' => true, 'submitOnChange' => true, ], - 'sql' => "varchar(10) NOT NULL default ''" + 'sql' => "varchar(10) NOT NULL default ''" ], 'attr_id' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['attr_id'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'attr_id.label', + 'description' => 'attr_id.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'tl_class' => 'w50', 'doNotSaveEmpty' => true, 'alwaysSave' => true, @@ -285,10 +301,11 @@ 'mandatory' => true, 'submitOnChange' => true, ], - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'tl_class' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['tl_class'], + 'label' => 'tl_class.label', + 'description' => 'tl_class.description', 'exclude' => true, 'inputType' => 'text', 'default' => 'w50', @@ -299,175 +316,209 @@ 'explanation' => 'tl_class', 'sql' => ['type' => 'string', 'length' => 64, 'default' => 'w50'] ], + 'be_template' => [ + 'label' => 'be_template.label', + 'description' => 'be_template.description', + 'exclude' => true, + 'inputType' => 'select', + 'sql' => 'varchar(255) NOT NULL default \'\'', + 'eval' => [ + 'includeBlankOption' => true, + 'tl_class' => 'clr w50', + 'chosen' => 'true' + ] + ], 'legendhide' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['legendhide'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'legendhide.label', + 'description' => 'legendhide.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12' ], - 'sql' => "varchar(5) NOT NULL default ''" + 'sql' => "varchar(5) NOT NULL default ''" ], 'legendtitle' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['legendtitle'], - 'exclude' => true, - 'eval' => [ + 'label' => 'legendtitle.label', + 'description' => 'legendtitle.description', + 'exclude' => true, + 'eval' => [ 'tl_class' => 'clr' ], - 'sql' => 'text NULL' + 'sql' => 'text NULL' ], 'mandatory' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['mandatory'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'mandatory.label', + 'description' => 'mandatory.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'alwaysSave' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['alwaysSave'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'alwaysSave.label', + 'description' => 'alwaysSave.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'filterable' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['filterable'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'filterable.label', + 'description' => 'filterable.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'searchable' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['searchable'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'searchable.label', + 'description' => 'searchable.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'chosen' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['chosen'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'chosen.label', + 'description' => 'chosen.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12' ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'allowHtml' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['allowHtml'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => + 'label' => 'allowHtml.label', + 'description' => 'allowHtml.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'preserveTags' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['preserveTags'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'preserveTags.label', + 'description' => 'preserveTags.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'decodeEntities' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['decodeEntities'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'decodeEntities.label', + 'description' => 'decodeEntities.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'rte' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rte'], - 'exclude' => true, - 'inputType' => 'select', - 'default' => 'tinyMCE', - 'eval' => [ + 'label' => 'rte.label', + 'description' => 'rte.description', + 'exclude' => true, + 'inputType' => 'select', + 'default' => 'tinyMCE', + 'eval' => [ 'tl_class' => 'w50', 'includeBlankOption' => true, ], - 'sql' => "varchar(64) NOT NULL default 'tinyMCE'" + 'sql' => "varchar(64) NOT NULL default 'tinyMCE'" ], 'rows' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rows'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => + 'label' => 'rows.label', + 'description' => 'rows.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'tl_class' => 'w50', 'rgxp' => 'digit' ], - 'sql' => "int(10) NOT NULL default '0'" + 'sql' => "int(10) NOT NULL default '0'" ], 'cols' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['cols'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'cols.label', + 'description' => 'cols.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'tl_class' => 'w50', 'rgxp' => 'digit' ], - 'sql' => "int(10) NOT NULL default '0'" + 'sql' => "int(10) NOT NULL default '0'" ], 'trailingSlash' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash'], - 'exclude' => true, - 'inputType' => 'select', - 'options' => [0, 1, 2], - 'default' => 2, - 'reference' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash_options'], - 'eval' => [ + 'label' => 'trailingSlash.label', + 'description' => 'trailingSlash.description', + 'exclude' => true, + 'inputType' => 'select', + 'options' => [0, 1, 2], + 'default' => 2, + 'reference' => [ + '0' => 'trailingSlash_options.0', + '1' => 'trailingSlash_options.1', + '2' => 'trailingSlash_options.2', + ], + 'eval' => [ 'tl_class' => 'clr w50', ], - 'sql' => "char(1) NOT NULL default '2'" + 'sql' => "char(1) NOT NULL default '2'" ], 'spaceToUnderscore' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['spaceToUnderscore'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'spaceToUnderscore.label', + 'description' => 'spaceToUnderscore.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'includeBlankOption' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['includeBlankOption'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'default' => '1', - 'eval' => [ + 'label' => 'includeBlankOption.label', + 'description' => 'includeBlankOption.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'default' => '1', + 'eval' => [ 'tl_class' => 'clr w50 cbx m12', ], - 'sql' => "char(1) NOT NULL default '1'" + 'sql' => "char(1) NOT NULL default '1'" ], 'submitOnChange' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['submitOnChange'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'submitOnChange.label', + 'description' => 'submitOnChange.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'clr w50 cbx m12', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'readonly' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['readonly'], - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'readonly.label', + 'description' => 'readonly.description', + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx m12', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ] ] ]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_dcasetting_condition.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_dcasetting_condition.php index 516289ed7..b0f6c31ce 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_dcasetting_condition.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_dcasetting_condition.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,19 +17,18 @@ * @author Ingolf Steinhardt * @author Richard Henkenjohann * @author Sven Baumann - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ -/** - * Table tl_metamodel_dcasetting_condition - */ +use ContaoCommunityAlliance\DcGeneral\DC\General; $GLOBALS['TL_DCA']['tl_metamodel_dcasetting_condition'] = [ 'config' => [ - 'dataContainer' => 'General', - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['list_label'], + 'dataContainer' => General::class, + 'label' => 'list_label.label', + 'description' => 'list_label.description', 'switchToEdit' => false, 'enableVersioning' => false, 'sql' => [ @@ -41,10 +40,10 @@ ], 'dca_config' => [ 'data_provider' => [ - 'root' => [ + 'root' => [ 'source' => 'tl_metamodel_dcasetting_condition' ], - 'parent' => [ + 'parent' => [ 'source' => 'tl_metamodel_dcasetting', ], 'tl_metamodel_dcasetting' => [ @@ -53,15 +52,15 @@ ], 'childCondition' => [ [ - 'from' => 'tl_metamodel_dcasetting', - 'to' => 'tl_metamodel_dcasetting_condition', - 'setOn' => [ + 'from' => 'tl_metamodel_dcasetting', + 'to' => 'tl_metamodel_dcasetting_condition', + 'setOn' => [ [ 'to_field' => 'settingId', 'from_field' => 'id', ] ], - 'filter' => [ + 'filter' => [ [ 'local' => 'settingId', 'remote' => 'id', @@ -77,9 +76,9 @@ ] ], [ - 'from' => 'tl_metamodel_dcasetting_condition', - 'to' => 'tl_metamodel_dcasetting_condition', - 'setOn' => [ + 'from' => 'tl_metamodel_dcasetting_condition', + 'to' => 'tl_metamodel_dcasetting_condition', + 'setOn' => [ [ 'to_field' => 'pid', 'from_field' => 'id', @@ -89,7 +88,7 @@ 'from_field' => 'settingId', ], ], - 'filter' => [ + 'filter' => [ [ 'local' => 'pid', 'remote' => 'id', @@ -156,45 +155,49 @@ ], 'global_operations' => [ 'all' => [ - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ] ], 'operations' => [ 'edit' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['edit'], - 'href' => 'act=edit', - 'icon' => 'edit.svg' + 'label' => 'edit.label', + 'description' => 'edit.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg' ], 'copy' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['copy'], - 'href' => 'act=copy', - 'icon' => 'copy.svg' + 'label' => 'copy.label', + 'description' => 'copy.description', + 'href' => 'act=copy', + 'icon' => 'copy.svg' ], 'cut' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['cut'], - 'href' => 'act=paste&mode=cut', - 'icon' => 'cut.svg', - 'attributes' => 'onclick="Backend.getScrollOffset()"', + 'label' => 'cut.label', + 'description' => 'cut.description', + 'href' => 'act=paste&mode=cut', + 'icon' => 'cut.svg', + 'attributes' => 'onclick="Backend.getScrollOffset()"', ], 'delete' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', ], 'show' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' ], 'toggle' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['toggle'], + 'label' => 'toggle.label', + 'description' => 'toggle.description', 'icon' => 'visible.svg', 'toggleProperty' => 'enabled', ] @@ -237,25 +240,31 @@ ], 'fields' => [ 'id' => [ - 'sql' => 'int(10) unsigned NOT NULL auto_increment' + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' ], 'pid' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'sorting' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'sorting.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'tstamp' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'settingId' => [ // Keep this empty but keep it here! // needed for act=copy in DC_Table, as otherwise the fid value will not be copied. - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['fid'], - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'fid.label', + 'description' => 'fid.description', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'type' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['type'], + 'label' => 'type.label', + 'description' => 'type.description', 'exclude' => true, 'inputType' => 'select', 'eval' => [ @@ -272,30 +281,33 @@ 'sql' => "varchar(255) NOT NULL default ''" ], 'enabled' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['enabled'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'default' => 1, - 'eval' => [ + 'label' => 'enabled.label', + 'description' => 'enabled.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'default' => 1, + 'eval' => [ 'alwaysSave' => true, 'tl_class' => 'w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'comment' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['comment'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'comment.label', + 'description' => 'comment.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'tl_class' => 'clr long' ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], 'attr_id' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['attr_id'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'attr_id.label', + 'description' => 'attr_id.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'doNotSaveEmpty' => true, 'alwaysSave' => true, 'submitOnChange' => true, @@ -304,19 +316,20 @@ 'tl_class' => 'w50', 'chosen' => true ], - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'value' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['value'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'value.label', + 'description' => 'value.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'alwaysSave' => true, 'includeBlankOption' => true, 'tl_class' => 'w50', 'chosen' => true ], - 'sql' => 'blob NULL' + 'sql' => 'blob NULL' ], ] ]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_filter.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_filter.php index 06dcb593d..9161a567b 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_filter.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_filter.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,216 +18,171 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Cliff Parnitzky - * @copyright 2012-2020 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ -/** - * Table tl_metamodel_attribute - */ +use ContaoCommunityAlliance\DcGeneral\DC\General; -$GLOBALS['TL_DCA']['tl_metamodel_filter'] = array -( - 'config' => array - ( - 'dataContainer' => 'General', - 'switchToEdit' => false, - 'enableVersioning' => false, - 'sql' => array - ( - 'keys' => array - ( - 'id' => 'primary', - 'pid' => 'index' - ), - ), - ), +$GLOBALS['TL_DCA']['tl_metamodel_filter'] = [ + 'config' => [ + 'dataContainer' => General::class, + 'switchToEdit' => false, + 'enableVersioning' => false, + 'sql' => [ + 'keys' => [ + 'id' => 'primary', + 'pid' => 'index' + ], + ], + ], + 'dca_config' => [ + 'data_provider' => [ + 'default' => [ + 'source' => 'tl_metamodel_filter' + ], + 'parent' => [ + 'source' => 'tl_metamodel' + ], - 'dca_config' => array - ( - 'data_provider' => array - ( - 'default' => array - ( - 'source' => 'tl_metamodel_filter' - ), - 'parent' => array - ( - 'source' => 'tl_metamodel' - ), - - 'tl_metamodel_filtersetting' => array - ( - 'source' => 'tl_metamodel_filtersetting' - ), - ), - 'childCondition' => array - ( - array - ( - 'from' => 'tl_metamodel', - 'to' => 'tl_metamodel_filter', - 'setOn' => array - ( - array - ( - 'to_field' => 'pid', - 'from_field' => 'id', - ), - ), - 'filter' => array - ( - array - ( - 'local' => 'pid', - 'remote' => 'id', - 'operation' => '=', - ), - ), - 'inverse' => array - ( - array - ( - 'local' => 'pid', - 'remote' => 'id', - 'operation' => '=', - ), - ) - ), + 'tl_metamodel_filtersetting' => [ + 'source' => 'tl_metamodel_filtersetting' + ], + ], + 'childCondition' => [ + [ + 'from' => 'tl_metamodel', + 'to' => 'tl_metamodel_filter', + 'setOn' => [ + [ + 'to_field' => 'pid', + 'from_field' => 'id', + ], + ], + 'filter' => [ + [ + 'local' => 'pid', + 'remote' => 'id', + 'operation' => '=', + ], + ], + 'inverse' => [ + [ + 'local' => 'pid', + 'remote' => 'id', + 'operation' => '=', + ], + ] + ], - array( + [ 'from' => 'tl_metamodel_filter', 'to' => 'tl_metamodel_filtersetting', - 'setOn' => array - ( - array - ( + 'setOn' => [ + [ 'to_field' => 'fid', 'from_field' => 'id', - ) - ), - 'filter' => array - ( - array - ( + ] + ], + 'filter' => [ + [ 'local' => 'fid', 'remote' => 'id', 'operation' => '=', - ), - ) - ), - ), - ), - - 'list' => array - ( - 'sorting' => array - ( - 'mode' => 4, - 'fields' => array - ( + ], + ] + ], + ], + ], + 'list' => [ + 'sorting' => [ + 'mode' => 4, + 'fields' => [ 'name' - ), - 'panelLayout' => 'filter,sort,limit', - 'headerFields' => array - ( + ], + 'panelLayout' => 'filter,sort,limit', + 'headerFields' => [ 'name' - ), - 'flag' => 1, - ), - - 'label' => array - ( - 'fields' => array - ( + ], + 'flag' => 1, + ], + 'label' => [ + 'fields' => [ 'name' - ), - 'format' => '%s' - ), - - 'global_operations' => array - ( - 'all' => array - ( - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' - ) - ), - - 'operations' => array - ( - 'edit' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filter']['edit'], - 'href' => 'act=edit', - 'icon' => 'edit.svg' - ), - 'delete' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filter']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) - ), - 'show' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filter']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' - ), - 'settings' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filter']['settings'], - 'href' => 'table=tl_metamodel_filtersetting', - 'idparam' => 'pid', - 'icon' => 'bundles/metamodelscore/images/icons/filter_setting.png', - ), - ) - ), - - 'metapalettes' => array - ( - 'default' => array - ( - 'title' => array - ( + ], + 'format' => '%s' + ], + 'global_operations' => [ + 'all' => [ + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' + ] + ], + 'operations' => [ + 'edit' => [ + 'label' => 'edit.label', + 'description' => 'edit.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg' + ], + 'delete' => [ + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', + ], + 'show' => [ + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' + ], + 'settings' => [ + 'label' => 'settings.label', + 'description' => 'settings.description', + 'href' => 'table=tl_metamodel_filtersetting', + 'idparam' => 'pid', + 'icon' => 'bundles/metamodelscore/images/icons/filter_setting.png', + ], + ] + ], + 'metapalettes' => [ + 'default' => [ + 'title' => [ 'name' - ) - ), - ), - - 'fields' => array - ( - 'id' => array - ( - 'sql' => 'int(10) unsigned NOT NULL auto_increment' - ), - 'pid' => array - ( - 'sql' => "int(10) unsigned NOT NULL default '0'" - ), - 'tstamp' => array - ( - 'sql' => "int(10) unsigned NOT NULL default '0'" - ), - 'name' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filter']['name'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => array - ( - 'mandatory' => true, - 'maxlength' => 255, - 'tl_class' => 'w50' - ), - 'sql' => "varchar(255) NOT NULL default ''" - ), - ) -); + ] + ], + ], + 'fields' => [ + 'id' => [ + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' + ], + 'pid' => [ + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" + ], + 'tstamp' => [ + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" + ], + 'name' => [ + 'label' => 'name.label', + 'description' => 'name.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ + 'mandatory' => true, + 'maxlength' => 255, + 'tl_class' => 'w50' + ], + 'sql' => "varchar(255) NOT NULL default ''" + ], + ] +]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_filtersetting.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_filtersetting.php index 72ae5eb2f..9fa4e1d74 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_filtersetting.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_filtersetting.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,19 +20,18 @@ * @author Ingolf Steinhardt * @author Richard Henkenjohann * @author Sven Baumann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ -/** - * Table tl_metamodel_attribute - */ +use ContaoCommunityAlliance\DcGeneral\DC\General; $GLOBALS['TL_DCA']['tl_metamodel_filtersetting'] = [ 'config' => [ - 'dataContainer' => 'General', - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['list_label'], + 'dataContainer' => General::class, + 'label' => 'list_label.label', + 'description' => 'list_label.description', 'switchToEdit' => false, 'enableVersioning' => false, 'sql' => [ @@ -53,15 +52,15 @@ ], 'childCondition' => [ [ - 'from' => 'tl_metamodel_filter', - 'to' => 'tl_metamodel_filtersetting', - 'setOn' => [ + 'from' => 'tl_metamodel_filter', + 'to' => 'tl_metamodel_filtersetting', + 'setOn' => [ [ 'to_field' => 'fid', 'from_field' => 'id', ] ], - 'filter' => [ + 'filter' => [ [ 'local' => 'fid', 'remote' => 'id', @@ -148,45 +147,49 @@ ], 'global_operations' => [ 'all' => [ - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ] ], 'operations' => [ 'edit' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['edit'], - 'href' => 'act=edit', - 'icon' => 'edit.svg' + 'label' => 'edit.label', + 'description' => 'edit.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg' ], 'copy' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['copy'], - 'href' => 'act=copy', - 'icon' => 'copy.svg' + 'label' => 'copy.label', + 'description' => 'copy.description', + 'href' => 'act=copy', + 'icon' => 'copy.svg' ], 'cut' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['cut'], - 'href' => 'act=paste&mode=cut', - 'icon' => 'cut.svg', - 'attributes' => 'onclick="Backend.getScrollOffset()"', + 'label' => 'cut.label', + 'description' => 'cut.description', + 'href' => 'act=paste&mode=cut', + 'icon' => 'cut.svg', + 'attributes' => 'onclick="Backend.getScrollOffset()"', ], 'delete' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', ], 'show' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' ], 'toggle' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['toggle'], + 'label' => 'toggle.label', + 'description' => 'toggle.description', 'icon' => 'visible.svg', 'toggleProperty' => 'enabled', ] @@ -256,30 +259,36 @@ ] ], 'fields' => [ - 'id' => [ - 'sql' => 'int(10) unsigned NOT NULL auto_increment' + 'id' => [ + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' ], - 'pid' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'pid' => [ + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'sorting' => [ + 'sorting' => [ + 'label' => 'sorting.label', 'sorting' => true, 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'tstamp' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'tstamp' => [ + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'fid' => [ + 'fid' => [ // Keep this empty but keep it here! // needed for act=copy in DC_Table, as otherwise the fid value will not be copied. - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['fid'], - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'fid.label', + 'description' => 'fid.description', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'type' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['type'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'type' => [ + 'label' => 'type.label', + 'description' => 'type.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'doNotSaveEmpty' => true, 'alwaysSave' => true, 'submitOnChange' => true, @@ -288,31 +297,34 @@ 'tl_class' => 'w50', 'chosen' => true ], - 'sql' => "varchar(64) NOT NULL default ''" + 'sql' => "varchar(64) NOT NULL default ''" ], - 'enabled' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['enabled'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'default' => 1, - 'eval' => [ + 'enabled' => [ + 'label' => 'enabled.label', + 'description' => 'enabled.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'default' => 1, + 'eval' => [ 'alwaysSave' => true, 'tl_class' => 'w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], - 'comment' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['comment'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => ['tl_class' => 'clr long'], - 'sql' => "varchar(255) NOT NULL default ''" + 'comment' => [ + 'label' => 'comment.label', + 'description' => 'comment.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => ['tl_class' => 'clr long'], + 'sql' => "varchar(255) NOT NULL default ''" ], - 'attr_id' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['attr_id'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'attr_id' => [ + 'label' => 'attr_id.label', + 'description' => 'attr_id.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'doNotSaveEmpty' => true, 'alwaysSave' => true, 'submitOnChange' => true, @@ -321,60 +333,66 @@ 'tl_class' => 'w50', 'chosen' => true ], - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'all_langs' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['all_langs'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'all_langs' => [ + 'label' => 'all_langs.label', + 'description' => 'all_langs.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'alwaysSave' => true, 'tl_class' => 'w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], - 'items' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['items'], - 'exclude' => true, - 'inputType' => 'textarea', - 'eval' => [ + 'items' => [ + 'label' => 'items.label', + 'description' => 'items.description', + 'exclude' => true, + 'inputType' => 'textarea', + 'eval' => [ 'doNotSaveEmpty' => true, 'alwaysSave' => true, 'mandatory' => true, ], - 'sql' => 'text NULL' + 'sql' => 'text NULL' ], - 'urlparam' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['urlparam'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'urlparam' => [ + 'label' => 'urlparam.label', + 'description' => 'urlparam.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'tl_class' => 'w50', ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], - 'predef_param' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['predef_param'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'predef_param' => [ + 'label' => 'predef_param.label', + 'description' => 'predef_param.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'alwaysSave' => true, 'tl_class' => 'w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], - 'fe_widget' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['fe_widget'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'fe_widget' => [ + 'label' => 'fe_widget.label', + 'description' => 'fe_widget.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'alwaysSave' => true, 'tl_class' => 'w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], - 'customsql' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['customsql'], + 'customsql' => [ + 'label' => 'customsql.label', + 'description' => 'customsql.description', 'exclude' => true, 'inputType' => 'textarea', 'default' => 'SELECT id FROM {{table}} @@ -390,146 +408,163 @@ 'explanation' => 'customsql', 'sql' => 'text NULL' ], - 'allow_empty' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['allow_empty'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'allow_empty' => [ + 'label' => 'allow_empty.label', + 'description' => 'allow_empty.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'alwaysSave' => true, 'tl_class' => 'w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], - 'stop_after_match' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['stop_after_match'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'stop_after_match' => [ + 'label' => 'stop_after_match.label', + 'description' => 'stop_after_match.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'alwaysSave' => true, 'tl_class' => 'w50 cbx', ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], - 'label' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['label'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => [ + 'label' => 'label.label', + 'description' => 'label.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'tl_class' => 'clr w50', ], - 'sql' => 'blob NULL' + 'sql' => 'blob NULL' ], - 'template' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['template'], - 'default' => 'mm_filteritem_default', - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'template' => [ + 'label' => 'template.label', + 'description' => 'template.description', + 'default' => 'mm_filteritem_default', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'tl_class' => 'w50', 'chosen' => true ], - 'sql' => "varchar(64) NOT NULL default ''" + 'sql' => "varchar(64) NOT NULL default ''" ], - 'blankoption' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['blankoption'], - 'exclude' => true, - 'default' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'blankoption' => [ + 'label' => 'blankoption.label', + 'description' => 'blankoption.description', + 'exclude' => true, + 'default' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'clr w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default '1'" + 'sql' => "char(1) NOT NULL default '1'" ], - 'onlyused' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['onlyused'], - 'exclude' => true, - 'default' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'onlyused' => [ + 'label' => 'onlyused.label', + 'description' => 'onlyused.description', + 'exclude' => true, + 'default' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 m12 cbx', 'submitOnChange' => true, ], - 'sql' => "char(1) NOT NULL default '0'" + 'sql' => "char(1) NOT NULL default '1'" ], - 'onlypossible' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['onlypossible'], - 'exclude' => true, - 'default' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'onlypossible' => [ + 'label' => 'onlypossible.label', + 'description' => 'onlypossible.description', + 'exclude' => true, + 'default' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default '0'" + 'sql' => "char(1) NOT NULL default '1'" ], - 'skipfilteroptions' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['skipfilteroptions'], - 'exclude' => true, - 'default' => false, - 'inputType' => 'checkbox', - 'eval' => [ + 'skipfilteroptions' => [ + 'label' => 'skipfilteroptions.label', + 'description' => 'skipfilteroptions.description', + 'exclude' => true, + 'default' => false, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default '0'" + 'sql' => "char(1) NOT NULL default ''" ], - 'defaultid' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['defaultid'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'defaultid' => [ + 'label' => 'defaultid.label', + 'description' => 'defaultid.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'tl_class' => 'clr w50', 'includeBlankOption' => true ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], - 'hide_label' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['hide_label'], - 'exclude' => true, - 'default' => false, - 'inputType' => 'checkbox', - 'eval' => [ + 'hide_label' => [ + 'label' => 'hide_label.label', + 'description' => 'hide_label.description', + 'exclude' => true, + 'default' => false, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default '0'" + 'sql' => "char(1) NOT NULL default '0'" ], 'label_as_blankoption' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['label_as_blankoption'], - 'exclude' => true, - 'default' => false, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'label_as_blankoption.label', + 'description' => 'label_as_blankoption.description', + 'exclude' => true, + 'default' => false, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 m12 cbx', ], - 'sql' => "char(1) NOT NULL default '0'" + 'sql' => "char(1) NOT NULL default '0'" ], - 'apply_sorting' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['apply_sorting'], - 'exclude' => true, - 'inputType' => 'select', - 'options' => ['natsort_asc', 'natsort_desc'], - 'reference' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['sorting_directions'], - 'eval' => [ + 'apply_sorting' => [ + 'label' => 'apply_sorting.label', + 'description' => 'apply_sorting.description', + 'exclude' => true, + 'inputType' => 'select', + 'options' => ['natsort_asc', 'natsort_desc'], + 'reference' => [ + 'natsort_asc' => 'sorting_directions.natsort_asc', + 'natsort_desc' => 'sorting_directions.natsort_desc', + ], + 'eval' => [ 'tl_class' => 'w50', 'includeBlankOption' => true ], - 'sql' => ['type' => 'string', 'length' => '24', 'notnull' => false, 'default' => ''] + 'sql' => ['type' => 'string', 'length' => '24', 'notnull' => false, 'default' => ''] ], - 'cssID' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['cssID'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'cssID' => [ + 'label' => 'cssID.label', + 'description' => 'cssID.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'multiple' => true, 'size' => 2, 'tl_class' => 'clr w50' ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], - 'placeholder' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['placeholder'], - 'exclude' => true, - 'inputType' => 'text', - 'sql' => 'varchar(255) NOT NULL default \'\'', - 'eval' => ['tl_class' => 'w50'] + 'placeholder' => [ + 'label' => 'placeholder.label', + 'description' => 'placeholder.description', + 'exclude' => true, + 'inputType' => 'text', + 'sql' => 'varchar(255) NOT NULL default \'\'', + 'eval' => ['tl_class' => 'w50'] ] ] ]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_item.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_item.php index 9d5fd0c39..d59a381c3 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_item.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_item.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author Oliver Lohoff * @author Stefan Heimes * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,61 +26,45 @@ This file defines the basic structure of ALL MetaModel items. Note however, that various MetaModel extensions might remove or add stuff here. */ - -$GLOBALS['TL_DCA']['tl_metamodel_item'] = array -( - 'config' => array - ( - 'dataContainer' => 'General', - 'switchToEdit' => false, - 'enableVersioning' => false, - ), - 'dca_config' => array - ( - 'data_provider' => array - ( - 'default' => array - ( - 'class' => 'MetaModels\DcGeneral\Data\Driver', - ) - ), - ), - - 'list' => array - ( - 'sorting' => array - ( +$GLOBALS['TL_DCA']['tl_metamodel_item'] = [ + 'config' => [ + 'dataContainer' => 'General', + 'switchToEdit' => false, + 'enableVersioning' => false, + ], + 'dca_config' => [ + 'data_provider' => [ + 'default' => [ + 'class' => 'MetaModels\DcGeneral\Data\Driver', + ] + ], + ], + 'list' => [ + 'sorting' => [ // This means: 1 default sorting value, 2 switchable sorting value. - 'mode' => 1, - 'headerFields' => array - ( + 'mode' => 1, + 'headerFields' => [ 'tstamp' - ), - ), - - 'label' => array - ( - 'fields' => array - ( - ), - 'format' => '%s', - ), - ), - - 'fields' => array - ( - 'id' => array - ( - ), - 'pid' => array - ( - ), - 'sorting' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_item']['sorting'], - ), - 'tstamp' => array - ( - ) - ) -); + ], + ], + 'label' => [ + 'fields' => [ + ], + 'format' => '%s', + ], + ], + 'fields' => [ + 'id' => [ + 'label' => 'id.label', + ], + 'pid' => [ + 'label' => 'pid.label', + ], + 'sorting' => [ + 'label' => 'sorting.label', + ], + 'tstamp' => [ + 'label' => 'tstamp.label', + ] + ] +]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersetting.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersetting.php index e1060923d..250f6df22 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersetting.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersetting.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,229 +20,191 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author David Molineus - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ -$GLOBALS['TL_DCA']['tl_metamodel_rendersetting'] = array -( - 'config' => array - ( +$GLOBALS['TL_DCA']['tl_metamodel_rendersetting'] = [ + 'config' => [ 'dataContainer' => 'General', 'ptable' => 'tl_metamodel_rendersettings', 'switchToEdit' => true, 'enableVersioning' => false, - 'sql' => array - ( - 'keys' => array - ( + 'sql' => [ + 'keys' => [ 'id' => 'primary', 'pid' => 'index' - ), - ) - ), - 'dca_config' => array - ( - 'data_provider' => array - ( - 'default' => array - ( + ], + ] + ], + 'dca_config' => [ + 'data_provider' => [ + 'default' => [ 'source' => 'tl_metamodel_rendersetting' - ), - 'parent' => array - ( + ], + 'parent' => [ 'source' => 'tl_metamodel_rendersettings' - ), - 'tl_metamodel' => array - ( + ], + 'tl_metamodel' => [ 'source' => 'tl_metamodel' - ) - ), - 'childCondition' => array - ( - array( - 'from' => 'tl_metamodel_rendersettings', - 'to' => 'tl_metamodel_rendersetting', - 'setOn' => array - ( - array - ( + ] + ], + 'childCondition' => [ + [ + 'from' => 'tl_metamodel_rendersettings', + 'to' => 'tl_metamodel_rendersetting', + 'setOn' => [ + [ 'to_field' => 'pid', 'from_field' => 'id', - ), - ), - 'filter' => array - ( - array - ( + ], + ], + 'filter' => [ + [ 'local' => 'pid', 'remote' => 'id', 'operation' => '=', - ), - ), - 'inverse' => array - ( - array - ( + ], + ], + 'inverse' => [ + [ 'local' => 'pid', 'remote' => 'id', 'operation' => '=', - ), - ) - ), - array( + ], + ] + ], + [ 'from' => 'tl_metamodel', 'to' => 'tl_metamodel_rendersettings', - 'setOn' => array - ( - array - ( + 'setOn' => [ + [ 'to_field' => 'pid', 'from_field' => 'id', - ), - ), - 'filter' => array - ( - array - ( + ], + ], + 'filter' => [ + [ 'local' => 'pid', 'remote' => 'id', 'operation' => '=', - ), - ), - 'inverse' => array - ( - array - ( + ], + ], + 'inverse' => [ + [ 'local' => 'pid', 'remote' => 'id', 'operation' => '=', - ), - ) - ) - ), - 'child_list' => array - ( - 'tl_metamodel_rendersetting' => array - ( - 'fields' => array - ( + ], + ] + ] + ], + 'child_list' => [ + 'tl_metamodel_rendersetting' => [ + 'fields' => [ 'type', 'attr_id', 'urlparam', 'comment' - ), + ], 'format' => '%s %s', - ), - ), - ), - 'list' => array - ( - 'sorting' => array - ( + ], + ], + ], + 'list' => [ + 'sorting' => [ 'mode' => 4, - 'fields' => array('sorting'), + 'fields' => ['sorting'], 'panelLayout' => 'limit', - 'headerFields' => array('name'), - ), - 'global_operations' => array - ( - 'addall' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall'], - 'class' => 'header_add_all rendersetting_add_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' - ), - 'all' => array - ( - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' - ) - ), - 'operations' => array - ( - 'edit' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit'], - 'href' => 'act=edit', - 'icon' => 'edit.svg' - ), - 'cut' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['cut'], - 'icon' => 'cut.svg' - ), - 'delete' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) - ), - 'show' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' - ), - 'toggle' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['toggle'], + 'headerFields' => ['name'], + ], + 'global_operations' => [ + 'addall' => [ + 'label' => 'addall.label', + 'description' => 'addall.description', + 'class' => 'header_add_all rendersetting_add_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' + ], + 'all' => [ + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' + ] + ], + 'operations' => [ + 'edit' => [ + 'label' => 'edit.label', + 'description' => 'edit.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg' + ], + 'cut' => [ + 'label' => 'cut.label', + 'description' => 'cut.description', + 'icon' => 'cut.svg' + ], + 'delete' => [ + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', + ], + 'show' => [ + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' + ], + 'toggle' => [ + 'label' => 'toggle.label', + 'description' => 'toggle.description', 'icon' => 'visible.svg', 'toggleProperty' => 'enabled', - ) - ) - ), - 'palettes' => array - ( - '__selector__' => array - ( + ] + ] + ], + 'palettes' => [ + '__selector__' => [ 'attr_id' - ) - ), - 'metapalettes' => array - ( - 'default' => array - ( - 'title' => array - ( + ] + ], + 'metapalettes' => [ + 'default' => [ + 'title' => [ 'attr_id', 'template', 'additional_class' - ) - ), - ), + ] + ], + ], // Fields. - 'fields' => array - ( - 'id' => array - ( - 'sql' => 'int(10) unsigned NOT NULL auto_increment' - ), - 'pid' => array - ( - 'sql' => "int(10) unsigned NOT NULL default '0'" - ), - 'sorting' => array - ( - 'sql' => "int(10) unsigned NOT NULL default '0'" - ), - 'tstamp' => array - ( - 'sql' => "int(10) unsigned NOT NULL default '0'" - ), - 'attr_id' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => array( + 'fields' => [ + 'id' => [ + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' + ], + 'pid' => [ + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" + ], + 'sorting' => [ + 'label' => 'sorting.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" + ], + 'tstamp' => [ + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" + ], + 'attr_id' => [ + 'label' => 'attr_id.label', + 'description' => 'attr_id.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'doNotSaveEmpty' => true, 'alwaysSave' => true, 'submitOnChange' => true, @@ -250,38 +212,37 @@ 'mandatory' => true, 'chosen' => true, 'tl_class' => 'w50' - ), - 'sql' => "int(10) unsigned NOT NULL default '0'" - ), - 'template' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => array - ( + ], + 'sql' => "int(10) unsigned NOT NULL default '0'" + ], + 'template' => [ + 'label' => 'template.label', + 'description' => 'template.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'tl_class' => 'w50', 'chosen' => true, 'includeBlankOption' => true, - ), - 'sql' => "varchar(64) NOT NULL default ''" - ), - 'additional_class' => array - ( - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['additional_class'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => array - ( + ], + 'sql' => "varchar(64) NOT NULL default ''" + ], + 'additional_class' => [ + 'label' => 'additional_class.label', + 'description' => 'additional_class.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'tl_class' => 'w50', 'maxlength' => 64, - ), - 'sql' => "varchar(64) NOT NULL default ''" - ), - 'enabled' => array - ( - 'default' => 1, - 'sql' => "char(1) NOT NULL default ''" - ) - ) -); + ], + 'sql' => "varchar(64) NOT NULL default ''" + ], + 'enabled' => [ + 'label' => 'enabled.label', + 'description' => 'enabled.description', + 'default' => 1, + 'sql' => "char(1) NOT NULL default ''" + ] + ] +]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersettings.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersettings.php index ec708ef56..636114838 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersettings.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_rendersettings.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,14 +21,16 @@ * @author Richard Henkenjohann * @author Ingolf Steinhardt * @author Cliff Parnitzky - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ +use ContaoCommunityAlliance\DcGeneral\DC\General; + $GLOBALS['TL_DCA']['tl_metamodel_rendersettings'] = [ 'config' => [ - 'dataContainer' => 'General', + 'dataContainer' => General::class, 'ptable' => 'tl_metamodel', 'switchToEdit' => false, 'enableVersioning' => false, @@ -41,13 +43,13 @@ ], 'dca_config' => [ 'data_provider' => [ - 'default' => [ + 'default' => [ 'source' => 'tl_metamodel_rendersettings' ], - 'parent' => [ + 'parent' => [ 'source' => 'tl_metamodel' ], - 'tl_metamodel_rendersetting' => [ + 'tl_metamodel_rendersetting' => [ 'source' => 'tl_metamodel_rendersetting' ], ], @@ -122,63 +124,66 @@ ], 'global_operations' => [ 'all' => [ - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ] ], 'operations' => [ 'edit' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['edit'], - 'href' => 'act=edit', - 'icon' => 'edit.svg' + 'label' => 'edit.label', + 'description' => 'edit.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg' ], 'copy' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['copy'], - 'href' => 'act=copy', - 'icon' => 'copy.svg' + 'label' => 'copy.label', + 'description' => 'copy.description', + 'href' => 'act=copy', + 'icon' => 'copy.svg' ], 'delete' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', ], 'show' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' ], 'settings' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['settings'], - 'href' => 'table=tl_metamodel_rendersetting', - 'icon' => 'bundles/metamodelscore/images/icons/rendersetting.png', - 'idparam' => 'pid' + 'label' => 'settings.label', + 'description' => 'settings.description', + 'href' => 'table=tl_metamodel_rendersetting', + 'icon' => 'bundles/metamodelscore/images/icons/rendersetting.png', + 'idparam' => 'pid' ], ] ], 'metapalettes' => [ 'default' => [ - 'title' => [ + 'title' => [ 'name' ], - 'general' => [ + 'general' => [ 'template', 'format' ], - 'expert' => [ + 'expert' => [ 'hideEmptyValues', 'hideLabels', ], - 'jumpto' => [ + 'jumpto' => [ 'jumpTo' ], - 'additional' => [ + 'additional' => [ ':hide', 'additionalCss', 'additionalJs' @@ -187,106 +192,122 @@ ], 'fields' => [ 'id' => [ - 'sql' => 'int(10) unsigned NOT NULL auto_increment' + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' ], 'pid' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'tstamp' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'name' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['name'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'name.label', + 'description' => 'name.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'mandatory' => true, 'maxlength' => 255, 'tl_class' => 'w50' ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], 'hideEmptyValues' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['hideEmptyValues'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'hideEmptyValues.label', + 'description' => 'hideEmptyValues.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx' ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'hideLabels' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['hideLabels'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'hideLabels.label', + 'description' => 'hideLabels.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'tl_class' => 'w50 cbx' ], - 'sql' => "char(1) NOT NULL default ''" + 'sql' => "char(1) NOT NULL default ''" ], 'template' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['template'], - 'default' => 'metamodel_prerendered', - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'template.label', + 'description' => 'template.description', + 'default' => 'metamodel_prerendered', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'includeBlankOption' => true, 'tl_class' => 'w50', 'mandatory' => true, 'chosen' => true ], - 'sql' => "varchar(64) NOT NULL default ''" + 'sql' => "varchar(64) NOT NULL default ''" ], 'format' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['format'], - 'exclude' => true, - 'inputType' => 'select', - 'options' => [ + 'label' => 'format.label', + 'description' => 'format.description', + 'exclude' => true, + 'inputType' => 'select', + 'options' => [ 'html5', 'text' ], - 'reference' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['formatOptions'], - 'eval' => [ + 'reference' => [ + 'html5' => 'formatOptions.html5', + 'text' => 'formatOptions.text', + ], + 'eval' => [ 'includeBlankOption' => true, 'tl_class' => 'w50', 'chosen' => true ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], 'jumpTo' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo'], + 'label' => 'jumpTo.label', + 'description' => 'jumpTo.description', 'exclude' => true, 'minCount' => 1, 'maxCount' => 1, 'disableSorting' => '1', 'inputType' => 'multiColumnWizard', 'eval' => [ - 'dragAndDrop' => false, - 'hideButtons' => true, - 'style' => 'width:100%;', - 'tl_class' => 'clr clx', - 'columnFields' => [ + 'useTranslator' => true, + 'dragAndDrop' => false, + 'hideButtons' => true, + 'style' => 'width:100%;', + 'tl_class' => 'clr clx', + 'columnFields' => [ 'langcode' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo_language'], - 'exclude' => true, - 'inputType' => 'justtextoption', - 'eval' => [ + 'label' => 'jumpTo_language.label', + 'description' => 'jumpTo_language.description', + 'exclude' => true, + 'inputType' => 'justtextoption', + 'eval' => [ 'valign' => 'center' ] ], 'value' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo_page'], - 'exclude' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'jumpTo_page.label', + 'description' => 'jumpTo_page.description', + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ 'style' => 'width:90%;' ] ], 'filter' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo_filter'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'jumpTo_filter.label', + 'description' => 'jumpTo_filter.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'style' => 'width:100%;', 'includeBlankOption' => true, 'chosen' => true @@ -297,64 +318,72 @@ 'sql' => 'blob NULL' ], 'additionalCss' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['additionalCss'], - 'exclude' => true, - 'inputType' => 'multiColumnWizard', - 'eval' => [ - 'style' => 'width:100%;', - 'tl_class' => 'w50', - 'columnFields' => [ + 'label' => 'additionalCss.label', + 'description' => 'additionalCss.description', + 'exclude' => true, + 'inputType' => 'multiColumnWizard', + 'eval' => [ + 'useTranslator' => true, + 'style' => 'width:100%;', + 'tl_class' => 'w50', + 'columnFields' => [ 'file' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['file'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'file.label', + 'description' => 'file.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'style' => 'width:100%;', 'chosen' => true, 'includeBlankOption' => true ] ], 'published' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['publish'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'publish.label', + 'description' => 'publish.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'style' => 'width:40px;' ] ], ] ], - 'sql' => 'blob NULL' + 'sql' => 'blob NULL' ], 'additionalJs' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['additionalJs'], - 'exclude' => true, - 'inputType' => 'multiColumnWizard', - 'eval' => [ - 'style' => 'width:100%;', - 'tl_class' => 'w50', - 'columnFields' => [ + 'label' => 'additionalJs.label', + 'description' => 'additionalJs.description', + 'exclude' => true, + 'inputType' => 'multiColumnWizard', + 'eval' => [ + 'useTranslator' => true, + 'style' => 'width:100%;', + 'tl_class' => 'w50', + 'columnFields' => [ 'file' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['file'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'file.label', + 'description' => 'file.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'style' => 'width:100%;', 'chosen' => true, 'includeBlankOption' => true ] ], 'published' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['publish'], - 'exclude' => true, - 'inputType' => 'checkbox', - 'eval' => [ + 'label' => 'publish.label', + 'description' => 'publish.description', + 'exclude' => true, + 'inputType' => 'checkbox', + 'eval' => [ 'style' => 'width:40px;' ] ], ] ], - 'sql' => 'blob NULL' + 'sql' => 'blob NULL' ], ], ]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_metamodel_searchable_pages.php b/src/CoreBundle/Resources/contao/dca/tl_metamodel_searchable_pages.php index dfcc6055b..1124a2683 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_metamodel_searchable_pages.php +++ b/src/CoreBundle/Resources/contao/dca/tl_metamodel_searchable_pages.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,14 +17,16 @@ * @author Richard Henkenjohann * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ +use ContaoCommunityAlliance\DcGeneral\DC\General; + $GLOBALS['TL_DCA']['tl_metamodel_searchable_pages'] = [ 'config' => [ - 'dataContainer' => 'General', + 'dataContainer' => General::class, 'ptable' => 'tl_metamodel', 'switchToEdit' => false, 'enableVersioning' => false, @@ -40,7 +42,7 @@ 'default' => [ 'source' => 'tl_metamodel_searchable_pages' ], - 'parent' => [ + 'parent' => [ 'source' => 'tl_metamodel' ] ], @@ -85,39 +87,42 @@ ], 'global_operations' => [ 'all' => [ - 'label' => &$GLOBALS['TL_LANG']['MSC']['all'], - 'href' => 'act=select', - 'class' => 'header_edit_all', - 'attributes' => 'onclick="Backend.getScrollOffset();"' + 'label' => 'all.label', + 'description' => 'all.description', + 'href' => 'act=select', + 'class' => 'header_edit_all', + 'attributes' => 'onclick="Backend.getScrollOffset();"' ], ], 'operations' => [ 'edit' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['edit'], - 'href' => 'act=edit', - 'icon' => 'edit.svg', + 'label' => 'edit.label', + 'description' => 'edit.description', + 'href' => 'act=edit', + 'icon' => 'edit.svg', ], 'copy' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['copy'], - 'href' => 'act=copy', - 'icon' => 'copy.svg', + 'label' => 'copy.label', + 'description' => 'copy.description', + 'href' => 'act=copy', + 'icon' => 'copy.svg', ], 'delete' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['delete'], - 'href' => 'act=delete', - 'icon' => 'delete.svg', - 'attributes' => sprintf( - 'onclick="if (!confirm(\'%s\')) return false; Backend.getScrollOffset();"', - $GLOBALS['TL_LANG']['MSC']['deleteConfirm'] - ) + 'label' => 'delete.label', + 'description' => 'delete.description', + 'href' => 'act=delete', + 'icon' => 'delete.svg', + 'attributes' => 'onclick="if (!confirm(this.dataset.msgConfirm)) return false; Backend.getScrollOffset();"', ], 'show' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['show'], - 'href' => 'act=show', - 'icon' => 'show.svg' + 'label' => 'show.label', + 'description' => 'show.description', + 'href' => 'act=show', + 'icon' => 'show.svg' ], 'toggle' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['toggle'], + 'label' => 'toggle.label', + 'description' => 'toggle.description', 'icon' => 'visible.svg', 'toggleProperty' => 'published' ] @@ -137,69 +142,78 @@ ], 'fields' => [ 'id' => [ - 'sql' => 'int(10) unsigned NOT NULL auto_increment' + 'label' => 'id.label', + 'sql' => 'int(10) unsigned NOT NULL auto_increment' + ], + 'pid' => [ + 'label' => 'pid.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'pid' => - [ - 'sql' => "int(10) unsigned NOT NULL default '0'" - ], 'tstamp' => [ - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'label' => 'tstamp.label', + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'name' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['name'], - 'exclude' => true, - 'search' => true, - 'inputType' => 'text', - 'eval' => [ + 'label' => 'name.label', + 'description' => 'name.description', + 'exclude' => true, + 'search' => true, + 'inputType' => 'text', + 'eval' => [ 'mandatory' => true, 'maxlength' => 255, 'tl_class' => 'w50' ], - 'sql' => "varchar(255) NOT NULL default ''" + 'sql' => "varchar(255) NOT NULL default ''" ], 'filter' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['filter'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'filter.label', + 'description' => 'filter.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'includeBlankOption' => true, 'chosen' => true, 'submitOnChange' => true, 'tl_class' => 'clr w50', ], - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'sql' => "int(10) unsigned NOT NULL default '0'" ], 'filterparams' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['filterparams'], - 'exclude' => true, - 'inputType' => 'mm_subdca', - 'eval' => [ + 'label' => 'filterparams.label', + 'description' => 'filterparams.description', + 'exclude' => true, + 'inputType' => 'mm_subdca', + 'eval' => [ 'tl_class' => 'clr m12', 'flagfields' => [ 'use_get' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['filterparams'], - 'inputType' => 'checkbox' + 'label' => 'filterparams.label', + 'description' => 'filterparams.description', + 'inputType' => 'checkbox' ], ], ], - 'sql' => 'longblob NULL' + 'sql' => 'longblob NULL' ], 'rendersetting' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['rendersetting'], - 'exclude' => true, - 'inputType' => 'select', - 'eval' => [ + 'label' => 'rendersetting.label', + 'description' => 'rendersetting.description', + 'exclude' => true, + 'inputType' => 'select', + 'eval' => [ 'includeBlankOption' => true, 'mandatory' => true, 'chosen' => true, 'tl_class' => 'w50', ], - 'sql' => "int(10) unsigned NOT NULL default '0'" + 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'published' => [ - 'default' => 1, - 'sql' => "char(1) NOT NULL default '1'" + 'published' => [ + 'label' => 'published.label', + 'description' => 'published.description', + 'default' => 1, + 'sql' => "char(1) NOT NULL default '1'" ] ] ]; diff --git a/src/CoreBundle/Resources/contao/dca/tl_module.php b/src/CoreBundle/Resources/contao/dca/tl_module.php old mode 100644 new mode 100755 index 53476fecf..e2cade3e1 --- a/src/CoreBundle/Resources/contao/dca/tl_module.php +++ b/src/CoreBundle/Resources/contao/dca/tl_module.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-20243 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,7 +18,7 @@ * @author Richard Henkenjohann * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +45,7 @@ '{title_legend},name,headline,type;' . '{mm_filter_legend},metamodel,metamodel_filtering,metamodel_fef_template,metamodel_fef_params,' . 'metamodel_fef_autosubmit,metamodel_fef_hideclearfilter,metamodel_available_values,' . - 'metamodel_jumpTo,metamodel_fef_urlfragment;' . + 'metamodel_jumpTo,metamodel_fef_id,metamodel_fef_urlfragment;' . '{protected_legend:hide},protected;' . '{expert_legend:hide},guests,cssID,space'; @@ -60,22 +60,22 @@ $GLOBALS['TL_DCA']['tl_module']['palettes']['__selector__'][] = 'metamodel_use_parameters'; // Insert new Subpalettes after position 1. -array_insert( +\Contao\ArrayUtil::arrayInsert( $GLOBALS['TL_DCA']['tl_module']['subpalettes'], 1, [ 'metamodel_use_limit' => 'metamodel_offset,metamodel_limit', - 'metamodel_sort_override' => 'metamodel_sort_param_type,metamodel_order_by_param,metamodel_order_dir_param', + 'metamodel_sort_override' => 'metamodel_sort_param_type,metamodel_order_by_param,metamodel_order_dir_param,metamodel_sort_urlfragment', 'metamodel_use_parameters' => 'metamodel_parameters' ] ); // Fields. -array_insert( +\Contao\ArrayUtil::arrayInsert( $GLOBALS['TL_DCA']['tl_module']['fields'], 1, [ - 'metamodel' => [ + 'metamodel' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel'], 'exclude' => true, 'inputType' => 'select', @@ -92,7 +92,7 @@ ], 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'metamodel_layout' => [ + 'metamodel_layout' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_layout'], 'exclude' => true, 'inputType' => 'select', @@ -103,7 +103,7 @@ ], 'sql' => "varchar(64) NOT NULL default ''" ], - 'metamodel_use_limit' => [ + 'metamodel_use_limit' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_use_limit'], 'exclude' => true, 'inputType' => 'checkbox', @@ -113,7 +113,7 @@ ], 'sql' => "char(1) NOT NULL default ''" ], - 'metamodel_limit' => [ + 'metamodel_limit' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_limit'], 'exclude' => true, 'inputType' => 'text', @@ -123,7 +123,7 @@ ], 'sql' => "smallint(5) NOT NULL default '0'" ], - 'metamodel_offset' => [ + 'metamodel_offset' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_offset'], 'exclude' => true, 'inputType' => 'text', @@ -133,7 +133,7 @@ ], 'sql' => "smallint(5) NOT NULL default '0'" ], - 'metamodel_sortby' => [ + 'metamodel_sortby' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_sortby'], 'exclude' => true, 'inputType' => 'select', @@ -145,7 +145,7 @@ ], 'sql' => "varchar(64) NOT NULL default ''" ], - 'metamodel_sortby_direction' => [ + 'metamodel_sortby_direction' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_sortby_direction'], 'exclude' => true, 'inputType' => 'select', @@ -158,7 +158,7 @@ ], 'sql' => "varchar(4) NOT NULL default ''" ], - 'metamodel_sort_override' => [ + 'metamodel_sort_override' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_sort_override'], 'exclude' => true, 'inputType' => 'checkbox', @@ -168,7 +168,7 @@ ], 'sql' => "char(1) NOT NULL default ''" ], - 'metamodel_sort_param_type' => [ + 'metamodel_sort_param_type' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_sort_param_type'], 'exclude' => true, 'inputType' => 'select', @@ -180,7 +180,7 @@ ], 'sql' => "varchar(64) NOT NULL default 'slug'" ], - 'metamodel_order_by_param' => [ + 'metamodel_order_by_param' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_order_by_param'], 'exclude' => true, 'inputType' => 'text', @@ -190,7 +190,7 @@ ], 'sql' => "varchar(64) NOT NULL default ''" ], - 'metamodel_order_dir_param' => [ + 'metamodel_order_dir_param' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_order_dir_param'], 'exclude' => true, 'inputType' => 'text', @@ -200,7 +200,17 @@ ], 'sql' => "varchar(64) NOT NULL default ''" ], - 'metamodel_filtering' => [ + 'metamodel_sort_urlfragment' => [ + 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_sort_urlfragment'], + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ + 'tl_class' => 'clr w50', + 'rgxp' => 'alias' + ], + 'sql' => "char(255) NOT NULL default ''" + ], + 'metamodel_filtering' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_filtering'], 'exclude' => true, 'inputType' => 'select', @@ -217,7 +227,7 @@ ], 'sql' => "int(10) NOT NULL default '0'" ], - 'metamodel_rendersettings' => [ + 'metamodel_rendersettings' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_rendersettings'], 'exclude' => true, 'inputType' => 'select', @@ -234,7 +244,7 @@ ], 'sql' => "int(10) NOT NULL default '0'" ], - 'metamodel_noparsing' => [ + 'metamodel_noparsing' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_noparsing'], 'exclude' => true, 'inputType' => 'checkbox', @@ -244,7 +254,7 @@ ], 'sql' => "char(1) NOT NULL default ''" ], - 'metamodel_page_param_type' => [ + 'metamodel_page_param_type' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_page_param_type'], 'exclude' => true, 'inputType' => 'select', @@ -256,7 +266,7 @@ ], 'sql' => "varchar(64) NOT NULL default 'slugNget'" ], - 'metamodel_page_param' => [ + 'metamodel_page_param' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_page_param'], 'exclude' => true, 'inputType' => 'text', @@ -266,7 +276,7 @@ ], 'sql' => "varchar(64) NOT NULL default ''" ], - 'metamodel_maxpaginationlinks' => [ + 'metamodel_maxpaginationlinks' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_maxpaginationlinks'], 'exclude' => true, 'inputType' => 'text', @@ -276,7 +286,7 @@ ], 'sql' => "smallint(5) NOT NULL default '0'" ], - 'metamodel_pagination' => [ + 'metamodel_pagination' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_pagination'], 'exclude' => true, 'inputType' => 'select', @@ -287,7 +297,7 @@ ], 'sql' => "varchar(64) NOT NULL default ''" ], - 'metamodel_pagination_urlfragment' => [ + 'metamodel_pagination_urlfragment' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_pagination_urlfragment'], 'exclude' => true, 'inputType' => 'text', @@ -297,7 +307,7 @@ ], 'sql' => "char(255) NOT NULL default ''" ], - 'metamodel_donotindex' => [ + 'metamodel_donotindex' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_donotindex'], 'exclude' => true, 'inputType' => 'checkbox', @@ -307,7 +317,7 @@ ], 'sql' => "char(1) NOT NULL default ''" ], - 'metamodel_available_values' => [ + 'metamodel_available_values' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_available_values'], 'exclude' => true, 'inputType' => 'checkbox', @@ -316,7 +326,7 @@ ], 'sql' => "char(1) NOT NULL default ''" ], - 'metamodel_filterparams' => [ + 'metamodel_filterparams' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_filterparams'], 'exclude' => true, 'inputType' => 'mm_subdca', @@ -332,7 +342,7 @@ ], 'sql' => 'longblob NULL' ], - 'metamodel_jumpTo' => [ + 'metamodel_jumpTo' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_jumpTo'], 'exclude' => true, 'inputType' => 'pageTree', @@ -342,8 +352,8 @@ ], 'sql' => "int(10) unsigned NOT NULL default '0'" ], - 'metamodel_fef_urlfragment' => [ - 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_fef_urlfragment'], + 'metamodel_fef_id' => [ + 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_fef_id'], 'exclude' => true, 'inputType' => 'text', 'eval' => [ @@ -352,7 +362,17 @@ ], 'sql' => "char(255) NOT NULL default ''" ], - 'metamodel_fef_params' => [ + 'metamodel_fef_urlfragment' => [ + 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_fef_urlfragment'], + 'exclude' => true, + 'inputType' => 'text', + 'eval' => [ + 'tl_class' => 'clr w50', + 'rgxp' => 'alias' + ], + 'sql' => "char(255) NOT NULL default ''" + ], + 'metamodel_fef_params' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_fef_params'], 'exclude' => true, 'inputType' => 'checkboxWizard', @@ -363,7 +383,7 @@ ], 'sql' => 'blob NULL' ], - 'metamodel_fef_autosubmit' => [ + 'metamodel_fef_autosubmit' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_fef_autosubmit'], 'exclude' => true, 'default' => '1', @@ -373,7 +393,7 @@ ], 'sql' => "char(1) NOT NULL default ''" ], - 'metamodel_fef_hideclearfilter' => [ + 'metamodel_fef_hideclearfilter' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_fef_hideclearfilter'], 'exclude' => true, 'inputType' => 'checkbox', @@ -382,7 +402,7 @@ ], 'sql' => "char(1) NOT NULL default ''" ], - 'metamodel_fef_template' => [ + 'metamodel_fef_template' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_fef_template'], 'exclude' => true, 'inputType' => 'select', @@ -393,7 +413,7 @@ ], 'sql' => "varchar(64) NOT NULL default ''" ], - 'metamodel_meta_title' => [ + 'metamodel_meta_title' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_meta_title'], 'exclude' => true, 'inputType' => 'select', @@ -405,7 +425,7 @@ ], 'sql' => "varchar(64) NOT NULL default ''" ], - 'metamodel_meta_description' => [ + 'metamodel_meta_description' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_meta_description'], 'exclude' => true, 'inputType' => 'select', @@ -417,7 +437,7 @@ ], 'sql' => "varchar(64) NOT NULL default ''" ], - 'metamodel_use_parameters' => [ + 'metamodel_use_parameters' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_use_parameters'], 'exclude' => true, 'inputType' => 'checkbox', @@ -427,7 +447,7 @@ ], 'sql' => "char(1) NOT NULL default ''" ], - 'metamodel_parameters' => [ + 'metamodel_parameters' => [ 'label' => &$GLOBALS['TL_LANG']['tl_module']['metamodel_parameters'], 'exclude' => true, 'inputType' => 'multiColumnWizard', diff --git a/src/CoreBundle/Resources/contao/dca/tl_user_group.php b/src/CoreBundle/Resources/contao/dca/tl_user_group.php index 8e9b389f6..6f29615da 100644 --- a/src/CoreBundle/Resources/contao/dca/tl_user_group.php +++ b/src/CoreBundle/Resources/contao/dca/tl_user_group.php @@ -17,7 +17,18 @@ * @filesource */ -use MetaModels\CoreBundle\Contao\Hooks\FixupUserGroupModules; +use ContaoCommunityAlliance\DcGeneral\Contao\Callback\Callbacks; -$GLOBALS['TL_DCA']['tl_user_group']['fields']['modules']['options_callback'] = - [FixupUserGroupModules::class, 'fixupModules']; +$prefCallback = $GLOBALS['TL_DCA']['tl_user_group']['fields']['alexf']['options_callback'] ?? null; +// Filter all MetaModels tables from user group permissions - only Admins MUST edit MetaModels. +$GLOBALS['TL_DCA']['tl_user_group']['fields']['alexf']['options_callback'] = +static function () use ($prefCallback): array { + $options = (null === $prefCallback) ? [] : Callbacks::call($prefCallback); + foreach (\array_keys($options) as $tableName) { + if (str_starts_with($tableName, 'tl_metamodel')) { + unset($options[$tableName]); + } + } + + return $options; +}; diff --git a/src/CoreBundle/Resources/contao/languages/da/default.php b/src/CoreBundle/Resources/contao/languages/da/default.php deleted file mode 100644 index 76e07c5f2..000000000 --- a/src/CoreBundle/Resources/contao/languages/da/default.php +++ /dev/null @@ -1,80 +0,0 @@ -Hilfe-Website finden Sie als Leitfaden zu MetaModels das Handbuch, alle Support-Kanäle, Videos und unseren Newsletter-Service.


Direkter Link zum MetaModels Handbuch...'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['main_headline'] = 'Bitte unterstützen Sie uns!'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['main_text'] = 'Als Entwickler dieses Projektes erhalten wir keine Vergütung für unsere Arbeit. Ein Großteil unserer wertvollen Zeit widmen wir dem MetaModels-Projekt. Jede Spende an das MetaModels-Projekt würde es uns ermöglichen, während unserer regulären Arbeitszeit an dem Projekt zu arbeiten, was die Entwicklung erheblich beschleunigen würde. Deshalb bitten wir um eine Unterstützung für das MetaModels-Projekt.

Wir garantieren, dass Spendengelder nur für dieses Projekt ausgegeben werden. Sollten Sie spezielle Wünsche oder Anforderungen haben, können Sie gerne ein Mitglied unseres Teamsanfragen, das Ihnen gerne weiterhilft.


Ein großes Dankeschön an alle bestehenden Supporter und Sponsoren. Bitte beachten Sie unsere Spendenseite. Ein solches umfangreiches Open Source Projekt konnten wir ohne die Unterstützung der Community nicht realisieren.

Mehr Infos'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['other_donations'] = 'Für Spenden besuchen Sie auch unsere Fundraisings oder beachten Sie Spenden für das Handbuch.'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['purpose'] = 'Verwendungszweck'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_filter']['0'] = 'Fi'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_filter']['1'] = 'Filtern'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_name']['0'] = 'MM'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_name']['1'] = 'MetaModel'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_render_setting']['0'] = 'Rs'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_render_setting']['1'] = 'Render-Einstellung'; -$GLOBALS['TL_LANG']['MSC']['noItemsMsg'] = 'Ihre Suche lieferte keine passenden Ergebnisse.'; -$GLOBALS['TL_LANG']['MSC']['no_theme'] = 'Globaler Gültigkeitsbereich '; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['filter']['0'] = 'Filtersets'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['limit']['0'] = 'Limitierung'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['search']['0'] = 'Suche'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['sort']['0'] = 'Sortierung'; -$GLOBALS['TL_LANG']['MSC']['random'] = 'Zufällig'; -$GLOBALS['TL_LANG']['MSC']['template_in_theme'] = '%s (%s)'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clr']['0'] = 'clr'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clr']['1'] = 'Hebt alle Floats auf.'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clx']['0'] = 'clx'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clx']['1'] = 'Entfernt die Voreinstellung "overflow:hidden". Bitte gemeinsam mit "clr" verwenden.'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['long']['0'] = 'long'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['long']['1'] = 'Sorgt dafür, dass das Eingabefeld zwei Spalten umfasst.'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['m12']['0'] = 'm12'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['m12']['1'] = 'Fügt dem Element einen oberen Abstand von 12 Pixeln hinzu (z.B. für einzelne Checkboxen).'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50']['0'] = 'w50'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50']['1'] = 'Setzt die Feldbreite auf 50% und floatet das Element (float:left).'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50x']['0'] = 'w50x'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50x']['1'] = 'Entfernt die Voreinstellung für eine feste Höhe. Bitte gemeinsam mit "w50" verwenden.'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['wizard']['0'] = 'wizard'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['wizard']['1'] = 'Verkürzt das Eingabefeld, damit genug Platz für den Wizard (z.B. einen Date Picker) ist.'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['action_add'] = '+'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['action_remove'] = '-'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['clear_all'] = 'Alle Filter zurücksetzen'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['do_not_filter'] = 'Nicht filtern'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['no_combinations'] = '(Keine passenden Kombinationen gefunden.)'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['select_all'] = 'Alles auswählen'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['submit'] = 'Filtern'; - diff --git a/src/CoreBundle/Resources/contao/languages/de/explain.php b/src/CoreBundle/Resources/contao/languages/de/explain.php deleted file mode 100644 index 2862d23d5..000000000 --- a/src/CoreBundle/Resources/contao/languages/de/explain.php +++ /dev/null @@ -1,102 +0,0 @@ - - Es ist erforderlich, dass diese Abfrage mindestens eine Spalte mit dem Namen "id" zurückgibt.
- Es ist nicht möglich, Berechnungen aus dem SQL an die Liste zu übergeben.
- Die Deklaration der Spaltennamen sollte mit dem Tabellenalias als Präfix eingegeben werden, z.B. t.name.'; -$GLOBALS['TL_LANG']['XPL']['customsql']['1']['0'] = 'Beispiel 1
Einfache Abfrage'; -$GLOBALS['TL_LANG']['XPL']['customsql']['1']['1'] = '
SELECT t.id FROM mm_mymetamodel AS t WHERE t.page_id=1
- Damit werden alle IDs aus der Tabelle mm_mymetamodel ausgewählt, mit page_id=1 - '; -$GLOBALS['TL_LANG']['XPL']['customsql']['2']['0'] = 'Beispiel 2
Tabellennamen einsetzen'; -$GLOBALS['TL_LANG']['XPL']['customsql']['2']['1'] = '
SELECT t.id FROM {{table}} AS t WHERE t.page_id=1
- Dies ist lediglich dasselbe wie in Beispiel 1, aber der Tabellenname des aktuellen MetaModel - (z.B.: das mm_mymetamodel von oben) wird in die Abfrage eingefügt.'; -$GLOBALS['TL_LANG']['XPL']['customsql']['3']['0'] = 'Inserttags'; -$GLOBALS['TL_LANG']['XPL']['customsql']['3']['1'] = 'Insert-Tags werden unterstützt. Bitte beachten, dass nicht alle Tags für alle Ausgaben verfügbar sein können. Falls eine Filtereinstellung wie zum Beispiel {{page::id}} benutzt wird, dann ist der Insert-Tag nur für einen Seitenaufruf im Frontend und nicht für einen RRS-Feed verfügbar.'; -$GLOBALS['TL_LANG']['XPL']['customsql']['4']['0'] = 'Sichere Inserttags'; -$GLOBALS['TL_LANG']['XPL']['customsql']['4']['1'] = 'Sichere Insert-Tags funktionieren wie normale Insert-Tags. Allerdings werden die Werte in der Abfrage escaped.
Eine unbedachte Nutzung kann daher zu unerwarteten Ergebnissen führen.
Die Notation für sichere Insert-Tags ist wie folgt:
{{secure::page::id}}
'; -$GLOBALS['TL_LANG']['XPL']['customsql']['5']['0'] = 'Parameterquellen'; -$GLOBALS['TL_LANG']['XPL']['customsql']['5']['1'] = 'Parameterquellen sind nach diesem Muster aufgebaut:
{{param::[source]?[query string]}}
Eine Quelle kann bestehen aus
  • get - HTTP GET Query-String
  • post - HTTP POST Feldern
  • session - einem beliebigen Feld aus der Contao-Session
  • filter - einen beliebigen ausgeführten Filterparameter (um Filterparameter zwischen Filtereinstellungen zu teilen).
Der Abfragestring wird wie ein normaler HTTP-Query-String als "name=wert"-Paar aufgebaut kann mit dem Zeichen & kombiniert werden und muss mindestens das Feld \'name\' enthalten. Einer oder mehrere der folgenden optionalen Schlüsselwörter können zusätzlich benutzt werden;
  • default - der zu benutzende Standardwert falls kein anderer zur Verfügung steht
  • aggregate - entweder "list" oder "set"
  • key - auf 1 setzen um den Schlüssel eines Array auszulesen (benötigt "aggregate").
  • recursive - auf 1 setzen um Arrays rekursiv auszulesen (benötigt "aggregate").
'; -$GLOBALS['TL_LANG']['XPL']['customsql']['6']['0'] = 'Beispiel 3
-Komplexe Filter, Parameter und Quellen nutzen'; -$GLOBALS['TL_LANG']['XPL']['customsql']['6']['1'] = '
SELECT t.id
-    FROM {{table}} AS t
-    WHERE t.catname={{param::get?name=category&default=defaultcat}}
-

- Dies ist dasselbe wie in Beispiel 2, aber jetzt verwenden wir einen Parameter aus dem "query"-String. -

-

- bei der Beispiel-URL wie diese: "http://example.org/list/category/demo.html"
- wäre das Query: "SELECT t.id FROM mm_demo AS t WHERE t.catname=\'demo\'" -

-

- Ist die URL: "http://example.org/list.html",
- wäre das Query: "SELECT t.id FROM mm_demo AS t WHERE t.catname=\'defaultcat\'" -

- '; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['0']['0'] = 'Panel-Optionen'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['0']['1'] = 'Fügen Sie eine oder mehrere Panel-Optionen hinzu und trennen Sie diese mit einem Komma (= Freiraum) oder Semikolon (= neue Zeile), z. B. "filter;search;sort,limit".'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['1']['0'] = 'Panel-Optionen zum Kopieren'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['1']['1'] = 'filter;search;sort,limit'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['2']['0'] = 'Filtern'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['2']['1'] = 'Zeige Filter in der Listendarstellung'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['3']['0'] = 'Suche'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['3']['1'] = 'Zeige Suche in der Listendarstellung'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['4']['0'] = 'Sortierung'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['4']['1'] = 'Zeige Sortierung in der Listendarstellung'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['5']['0'] = 'Limit'; -$GLOBALS['TL_LANG']['XPL']['dca_panellayout']['5']['1'] = 'Zeige Limit in der Listendarstellung'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['0']['0'] = 'Eigenschaftswert ist gleich ...'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['0']['1'] = 'Die Bedingung ist erfüllt, wenn der Attributwert gleich dem festgelegten Wert ist. Als Attribute können diejenigen mit Einfachauswahl wie z.B. Select oder Checkbox ausgewählt werden.'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['1']['0'] = 'Eigenschaftswert beinhaltet ...'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['1']['1'] = 'Die Bedingung ist erfüllt, wenn ein beliebiger Attributwert gleich dem jeweils festgelegten Wert ist (Schnittmenge bzw. ODER). Als Attribute können diejenigen mit Mehrfachauswahl wie z.B. Tags ausgewählt werden.'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['2']['0'] = 'Eigenschaft ist sichtbar ...'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['2']['1'] = 'Die Bedingung ist erfüllt, wenn alle Bedingungen für ein ausgewähltes Attribut erfüllt sind. Mit anderen Worten, das Attribut ist sichtbar, und nur dann, wenn das ausgewählte (oder "referenzierte") Attribut auch sichtbar ist. Mit diesem Bedingungstyp erspart man sich das Duplizieren von erstellten Ansichtsbedingungen eines Attributs.'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['3']['0'] = 'ODER'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['3']['1'] = 'Eine beliebige Bedingung muss erfüllt sein.'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['4']['0'] = 'UND'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['4']['1'] = 'Alle Bedingungen müssen erfüllt sein.'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['5']['0'] = 'NICHT'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['5']['1'] = 'Kehrt das Ergebnis einer vorgegebenen Bedingung um.'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['0']['0'] = 'Die CSS-Klasse für das Eingabewidget setzen'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['0']['1'] = 'Legen Sie eine oder mehrere der folgenden CSS-Klassen fest, um das Layout des Widgets wie "clr w50" zu definieren.'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['1']['0'] = 'CSS-Klassen zum Kopieren'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['1']['1'] = 'clr clx w50 w50x m12 wizard long'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['0'] = 'clr'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['1'] = 'Hebt alle Floats auf.'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['0'] = 'clx'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['1'] = 'Entfernt die Voreinstellung "overflow:hidden". Bitte gemeinsam mit "clr" verwenden.'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['0'] = 'w50'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['1'] = 'Setzt die Feldbreite auf 50% und floated links (float:left).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['0'] = 'w50x'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['1'] = 'Entfernt die Voreinstellung für eine feste Höhe. Bitte gemeinsam mit "w50" verwenden.'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['0'] = 'm12'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['1'] = 'Fügt dem Element einen oberen Abstand von 12 Pixeln hinzu (z.B. für einzelne Checkboxen).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['0'] = 'wizard'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['1'] = 'Verkürzt das Eingabefeld, damit genug Platz für den Wizard (z.B. einen Date Picker) ist.'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['8']['0'] = 'long'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['8']['1'] = 'Setzt das Feld auf 100% Breite.'; - diff --git a/src/CoreBundle/Resources/contao/languages/de/modules.php b/src/CoreBundle/Resources/contao/languages/de/modules.php deleted file mode 100644 index dfd63cf1a..000000000 --- a/src/CoreBundle/Resources/contao/languages/de/modules.php +++ /dev/null @@ -1,25 +0,0 @@ -/orderDir/.html\' oder per GET-Parameter überschrieben werden.'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_sort_param_type']['0'] = 'URL-Typ für Parameter'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_sort_param_type']['1'] = 'Bitte wählen Sie den Typ der URL Parameter als Slug (key/value) oder GET (key=value).'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_sortby']['0'] = 'Sortieren nach'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_sortby']['1'] = 'Bitte wählen Sie eine Reihenfolge für die Sortierung aus.'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_sortby_direction']['0'] = 'Sortierreihenfolge'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_sortby_direction']['1'] = 'In aufsteigender oder absteigender Reihenfolge'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_use_limit']['0'] = 'Offset und Limit für die Auflistung verwenden'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_use_limit']['1'] = 'Auswählen, falls Sie die Anzahl anzuzeigender Items begrenzen möchten. Diese Einstellung ist beipielsweise nützlich, wenn Sie nur die ersten 500 Items anzeigen möchten, oder falls Sie alle Items anzeigen und dabei die ersten 10 überspringen wollen. Eine voreingestellte Paginierung bleibt dabei erhalten.'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_use_parameters']['0'] = 'Parameter aktivieren'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_use_parameters']['1'] = 'Sie können die Parameterliste aktivieren, um Ihre eigenen Parameter einzustellen.'; -$GLOBALS['TL_LANG']['tl_content']['mm_config_legend'] = 'MetaModel-Einstellungen'; -$GLOBALS['TL_LANG']['tl_content']['mm_filter_legend'] = 'MetaModel-Filter'; -$GLOBALS['TL_LANG']['tl_content']['mm_meta_legend'] = 'Suchmaschinen-Optimierung'; -$GLOBALS['TL_LANG']['tl_content']['mm_pagination_legend'] = 'MetaModel Paginierung'; -$GLOBALS['TL_LANG']['tl_content']['mm_parameters_legend'] = 'Parameter-Einstellungen'; -$GLOBALS['TL_LANG']['tl_content']['mm_rendering_legend'] = 'MetaModel Render-Einstellung'; -$GLOBALS['TL_LANG']['tl_content']['mm_sorting_legend'] = 'MetaModel Sortierung'; - diff --git a/src/CoreBundle/Resources/contao/languages/de/tl_form_field.php b/src/CoreBundle/Resources/contao/languages/de/tl_form_field.php deleted file mode 100644 index 504f575ee..000000000 --- a/src/CoreBundle/Resources/contao/languages/de/tl_form_field.php +++ /dev/null @@ -1,28 +0,0 @@ -Hinweis: Diese Option wird automatisch gesetzt, wenn in der Attributkonfiguration "Eindeutige Werte" ausgewählt wurde.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['mandatory_for_unique_attr'] = 'Eindeutige (unique) Attribute sind automatisch Pflichtfelder (Einstellung nicht änderbar).'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['name_langcode'] = 'Sprache'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['name_value'] = 'Legenden-Titel'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['new']['0'] = 'Neu'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['new']['1'] = 'Neue Einstellung erstellen.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['overview_legend'] = 'Filtern und Suchen in der Backend-Liste'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pasteafter']['0'] = 'Neue Einstellung oben anlegen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pasteafter']['1'] = 'Neue Einstellung nach Element ID %s anlegen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pastenew']['0'] = 'Neue Einstellung oben anlegen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pastenew']['1'] = 'Neues Einstellung nach dem Element ID %s anlegen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['presentation_legend'] = 'Anzeigeoptionen des Widgets'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['preserveTags']['0'] = 'Alle HTML-Tags nicht encodieren'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['preserveTags']['1'] = 'Falls angewählt, werden keine HTML-Tags encodiert.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['readonly']['0'] = 'Nur lesen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['readonly']['1'] = 'Wenn aktiviert, erlaubt das Feld nur das Lesen und kann nicht geändert werden.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['readonly_for_force_alias'] = 'Attribute mit "Neuerstellung erzwingen" sind automatisch "nur lesend" (Einstellung nicht änderbar).'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rows']['0'] = 'Zeilen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rows']['1'] = 'Anzahl der Zeilen, die für longtext / table Widget verwendet werden.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rte']['0'] = 'Richtext-Editor aktivieren'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rte']['1'] = 'Wählen Sie eine Voreinstellung für den Richtext-Editor aus, die für dieses Feld benutzt werden soll (falls vorhanden).'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['searchable']['0'] = 'Suchbar'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['searchable']['1'] = 'Auswählen, falls dieses Attribut für die Suche im Backend zur Verfügung stehen soll.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['show']['0'] = 'Einstellungsdetails'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['show']['1'] = 'Zeige Details der Einstellung ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['spaceToUnderscore']['0'] = 'Leerzeichen durch Unterstriche ersetzen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['spaceToUnderscore']['1'] = 'Falls diese Option angewählt ist, werden Leerzeichen durch Unterstriche ersetzt.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['submitOnChange']['0'] = 'Absenden bei Änderungen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['submitOnChange']['1'] = 'Wenn aktiv, wird das Formular bei Änderungen eines Feldwertes abgesendet.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['template']['0'] = 'Angepasstes Template für die Ausgabe'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['template']['1'] = 'Wählen Sie das Template aus, das für das gewählte Attribut verwendet werden soll. Gültige Dateinamen für das Template beginnen mit "mm_<typ>", wobei <typ> für den Typ steht.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['title_legend'] = 'Typ'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['tl_class']['0'] = 'Backend-Klasse'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['tl_class']['1'] = 'Hier können Sie eine oder mehrere Backend-Klassen festlegen.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['toggle']['0'] = 'Wechseln'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['toggle']['1'] = 'Umschaltung des Status der Einstellung ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash']['0'] = 'Führende \'/\' bearbeiten'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash']['1'] = 'Hier können Sie angeben, wie führende \'/\' behandelt werden sollen.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash_options']['0'] = 'Schrägstrich beim Speichern entfernen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash_options']['1'] = 'Schrägstrich beim Speichern hinzufügen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash_options']['2'] = 'Nichts tun'; - diff --git a/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_dcasetting_condition.php b/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_dcasetting_condition.php deleted file mode 100644 index 633915bcd..000000000 --- a/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_dcasetting_condition.php +++ /dev/null @@ -1,70 +0,0 @@ -%s
für Attribut %s (Parameter: %s)'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['typedesc']['conditionand'] = '%s %s
alle Bedingungen müssen erfüllt sein.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['typedesc']['conditionnot'] = '%s %s
kehrt das Ergebnis der vorgegebenen Bedingung um.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['typedesc']['conditionor'] = '%s %s
eine beliebige Bedingung muss erfüllt sein.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['value']['0'] = 'Wert'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['value']['1'] = 'Bitte wählen Sie einen eigenen Wert aus.'; - diff --git a/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_filter.php b/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_filter.php deleted file mode 100644 index ba1f4e4ab..000000000 --- a/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_filter.php +++ /dev/null @@ -1,35 +0,0 @@ -[%s, "%s"]'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['_comment_'] = '
%s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['_default_'] = '%1$s %2$s%3$s%5$s%4$s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['_url_'] = ' (URL: %s)'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['conditionand'] = '%1$s %2$s%4$s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['conditionor'] = '%1$s %2$s%4$s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['fefilter'] = '%1$s %2$s %3$s%4$s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['simplelookup'] = '%1$s %2$s%3$s%5$s%4$s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typenames']['conditionand'] = 'UND-Bedingung (AND)'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typenames']['conditionor'] = 'ODER-Bedingung (OR)'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typenames']['customsql'] = 'Eigenes SQL'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typenames']['idlist'] = 'Vordefinierter Satz von Items'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typenames']['simplelookup'] = 'Einfache Abfrage'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['urlparam']['0'] = 'URL-Parameter'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['urlparam']['1'] = 'Geben Sie den URL-Parameter an, der dem ausgewählten Attribut zugeordnet werden soll. Der "auto_item"-Parameter kann ebenfalls verwendet werden. Diese Einstellung ist nützlich um Aliase zu verwenden.'; - diff --git a/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_item.php b/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_item.php deleted file mode 100644 index 0e4bb78e4..000000000 --- a/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_item.php +++ /dev/null @@ -1,52 +0,0 @@ -%s [%s]'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addAll_activate'] = 'Hinzugefügte Attribute aktivieren.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addAll_addsuccess'] = 'Das Attribut "%s" [%s] wurde der Render-Einstellung hinzugefügt.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addAll_alreadycontained'] = 'Das Attribut "%s" [%s] ist bereits in der Render-Einstellung vorhanden.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addAll_willadd'] = 'Fügt das Attribut "%s" [%s] den Render-Einstellung hinzu.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['0'] = 'Alle hinzufügen'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['1'] = 'Alle Attribute der Render-Einstellung hinzufügen.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['additional_class']['0'] = 'Eigene CSS-Klasse'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['additional_class']['1'] = 'Eingabe einer CSS-Klasse, wenn diese mit dem Attribut ausgegeben werden soll.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['advanced_legend'] = 'Erweitern'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['0'] = 'Attribut'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['1'] = 'Attribut, auf das sich diese Einstellung bezieht.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['copy']['0'] = 'Render-Einstellung kopieren'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['copy']['1'] = 'Kopieren Sie die Render-Einstellung ID %s.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['cut']['0'] = 'Render-Einstellung ausschneiden'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['cut']['1'] = 'Ausschneiden der Render-Einstellung ID %s.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['delete']['0'] = 'Render-Einstellung löschen'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['delete']['1'] = 'Löschen Sie die Render-Einstellung ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit']['0'] = 'Einstellung bearbeiten'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit']['1'] = 'Render-Einstellung ID %s bearbeiten.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['0'] = 'Neu'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['1'] = 'Neue Einstellung erstellen.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['pastenew']['0'] = 'Neue Einstellung oben erstellen'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['pastenew']['1'] = 'Neues Einstellung nach Render-Einstellung ID %s anlegen'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['show']['0'] = 'Details der Render-Einstellung'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['show']['1'] = 'Anzeige Details der Render-Einstellung ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['0'] = 'Angepasstes Template für die Ausgabe'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['1'] = 'Wählen Sie das Template aus, das für das gewählte Attribut verwendet werden soll. Gültige Dateinamen für das Template beginnen mit "mm_<typ>", wobei <typ> für den Typ steht.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['title_legend'] = 'Typ'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['toggle']['0'] = 'Wechseln'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['toggle']['1'] = 'Umschaltung des Status für die Render-Einstellung ID %s.'; - diff --git a/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_rendersettings.php b/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_rendersettings.php deleted file mode 100644 index bba6b80ea..000000000 --- a/src/CoreBundle/Resources/contao/languages/de/tl_metamodel_rendersettings.php +++ /dev/null @@ -1,75 +0,0 @@ -/orderDir/.html\' oder per GET-Parameter überschrieben werden.'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_sort_param_type']['0'] = 'URL-Typ für Parameter'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_sort_param_type']['1'] = 'Bitte geben Sie den Typ der URL-Parameter als Slug (key/value) oder GET (key=value) an.'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_sortby']['0'] = 'Sortieren nach'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_sortby']['1'] = 'Bitte wählen Sie eine Reihenfolge für die Sortierung aus.'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_sortby_direction']['0'] = 'Sortierreihenfolge'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_sortby_direction']['1'] = 'In aufsteigender oder absteigender Reihenfolge'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_use_limit']['0'] = 'Offset und Limit für die Auflistung verwenden'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_use_limit']['1'] = 'Auswählen, falls Sie die Anzahl anzuzeigender Datensätze begrenzen möchten. Diese Einstellung ist beispielsweise nützlich, wenn Sie nur die ersten 500 Datensätze anzeigen möchten, oder falls Sie alle Datensätze anzeigen und dabei die ersten 10 überspringen wollen. Eine voreingestellte Paginierung bleibt dabei erhalten.'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_use_parameters']['0'] = 'Parameter aktivieren'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_use_parameters']['1'] = 'Sie können die Parameterliste aktivieren, um Ihre eigenen Parameter einzustellen.'; -$GLOBALS['TL_LANG']['tl_module']['mm_config_legend'] = 'MetaModel-Einstellungen'; -$GLOBALS['TL_LANG']['tl_module']['mm_filter_legend'] = 'MetaModel-Filter'; -$GLOBALS['TL_LANG']['tl_module']['mm_meta_legend'] = 'Suchmaschinen-Optimierung'; -$GLOBALS['TL_LANG']['tl_module']['mm_pagination_legend'] = 'MetaModel Paginierung'; -$GLOBALS['TL_LANG']['tl_module']['mm_parameters_legend'] = 'Parameter-Einstellungen'; -$GLOBALS['TL_LANG']['tl_module']['mm_rendering_legend'] = 'MetaModel Render-Einstellung'; -$GLOBALS['TL_LANG']['tl_module']['mm_sorting_legend'] = 'MetaModel Sortierung'; - diff --git a/src/CoreBundle/Resources/contao/languages/de/tl_syncCto_database.php b/src/CoreBundle/Resources/contao/languages/de/tl_syncCto_database.php deleted file mode 100644 index ef6246bd8..000000000 --- a/src/CoreBundle/Resources/contao/languages/de/tl_syncCto_database.php +++ /dev/null @@ -1,23 +0,0 @@ -απλό ερώτημα '; -$GLOBALS['TL_LANG']['XPL']['customsql']['2']['0'] = 'Παραδειγμα 2
εισαγωγη ονοματος ταμπελας'; -$GLOBALS['TL_LANG']['XPL']['customsql']['3']['0'] = 'Εισαγωγη ετικετων'; -$GLOBALS['TL_LANG']['XPL']['customsql']['4']['0'] = 'Ασφαλης εισαγωγη ετικετων'; -$GLOBALS['TL_LANG']['XPL']['customsql']['5']['0'] = 'Πηγες παραμετρων
'; -$GLOBALS['TL_LANG']['XPL']['customsql']['6']['0'] = 'Παραδειγμα 3
χρησιμοποιηση φιλτρων των παραμετρων πηγων'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['3']['0'] = 'OR'; - diff --git a/src/CoreBundle/Resources/contao/languages/el/modules.php b/src/CoreBundle/Resources/contao/languages/el/modules.php deleted file mode 100644 index 437f62dae..000000000 --- a/src/CoreBundle/Resources/contao/languages/el/modules.php +++ /dev/null @@ -1,26 +0,0 @@ -%s [%s]'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['0'] = 'Προσθηκη ολων'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['1'] = ' -Προσθέστε όλα τα χαρακτηριστικά για να καταστήσει τη ρύθμιση '; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['advanced_legend'] = 'Προσαρμογη'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['0'] = ' Χαρακτηριστικο'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['1'] = 'Χαρακτηριστικό που η ρύθμιση αυτή το αφορά.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit']['0'] = 'Επεξεργασια ρυθμισης'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['0'] = 'Νεος τυπος'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['1'] = 'Δημιουργια νεας ρυθμισης'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['0'] = 'Προσαρμοσμένο πρότυπο που θα χρησιμοποιηθεί για την παραγωγή '; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['1'] = 'Επιλέξτε το πρότυπο που πρέπει να χρησιμοποιηθεί για την επιλεγμένη ιδιότητα. Ισχύει για αρχεία προτύπων αρχίζουν με "mm_<type>" όπου το όνομα του τύπου τίθεται για <type>'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['title_legend'] = 'Τυπος'; - diff --git a/src/CoreBundle/Resources/contao/languages/el/tl_metamodel_rendersettings.php b/src/CoreBundle/Resources/contao/languages/el/tl_metamodel_rendersettings.php deleted file mode 100644 index 0160600de..000000000 --- a/src/CoreBundle/Resources/contao/languages/el/tl_metamodel_rendersettings.php +++ /dev/null @@ -1,64 +0,0 @@ - * @author David Molineus * @author Sven Baumann - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ -$GLOBALS['TL_LANG']['CTE']['metamodels'] = 'MetaModel elements'; -$GLOBALS['TL_LANG']['CTE']['metamodel_content'][0] = 'MetaModel list'; -$GLOBALS['TL_LANG']['CTE']['metamodel_content'][1] = +$GLOBALS['TL_LANG']['CTE']['metamodels'] = 'MetaModels elements'; +$GLOBALS['TL_LANG']['CTE']['metamodel_content'][0] = 'MetaModels list'; +$GLOBALS['TL_LANG']['CTE']['metamodel_content'][1] = 'Adds a list of MetaModel items to the article.'; -$GLOBALS['TL_LANG']['CTE']['metamodels_frontendfilter'][0] = 'MetaModel frontend filter'; -$GLOBALS['TL_LANG']['CTE']['metamodels_frontendfilter'][1] = +$GLOBALS['TL_LANG']['CTE']['metamodels_frontendfilter'][0] = 'MetaModels frontend filter'; +$GLOBALS['TL_LANG']['CTE']['metamodels_frontendfilter'][1] = 'Adds a frontend filter for a MetaModel.'; -$GLOBALS['TL_LANG']['CTE']['metamodels_frontendclearall'][0] = 'MetaModel clear all'; -$GLOBALS['TL_LANG']['CTE']['metamodels_frontendclearall'][1] = +$GLOBALS['TL_LANG']['CTE']['metamodels_frontendclearall'][0] = 'MetaModels clear all'; +$GLOBALS['TL_LANG']['CTE']['metamodels_frontendclearall'][1] = 'Adds a clear all for all frontend filter.'; -$GLOBALS['TL_LANG']['MSC']['metamodel_filtersetting']['editRecord'] = - 'Edit filter setting %%s for filter "%s" in MetaModel "%s"'; -$GLOBALS['TL_LANG']['MSC']['metamodel_filtersetting']['label'] = 'Filter "%s" in MetaModel "%s"'; -$GLOBALS['TL_LANG']['MSC']['metamodel_rendersetting']['editRecord'] = - 'Edit attribute setting %%s for render setting "%s" in MetaModel "%s"'; -$GLOBALS['TL_LANG']['MSC']['metamodel_rendersetting']['label'] = - 'Render setting "%s" in MetaModel "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel'] = 'MetaModels'; -$GLOBALS['TL_LANG']['BRD']['metamodel_attribute'] = 'Attributes of "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel_rendersettings'] = 'All render setting of "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel_rendersetting'] = 'Render settings in "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel_dca_sortgroup'] = 'Sorting and grouping in "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel_dca'] = 'All input screens of "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel_dcasetting'] = 'Input screens in "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel_dca_combine'] = - 'Input screen and render setting combination for "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel_dcasetting_condition'] = - 'Visibility conditions for attribute "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel_filter'] = 'All filter of "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel_filtersetting'] = 'Filter settings in "%s"'; -$GLOBALS['TL_LANG']['BRD']['metamodel_searchable_pages'] = 'All indexes of "%s"'; -$GLOBALS['TL_LANG']['MSC']['metamodel_edit_as_child']['label'] = 'Edit "%s" for item %%s'; -$GLOBALS['TL_LANG']['MSC']['metamodels_sorting'] = 'Sorting'; -$GLOBALS['TL_LANG']['MSC']['random'] = 'Random'; -$GLOBALS['TL_LANG']['MSC']['template_in_theme'] = '%s (%s)'; -$GLOBALS['TL_LANG']['MSC']['no_theme'] = 'global scope'; -$GLOBALS['TL_LANG']['MSC']['noItemsMsg'] = - 'There are no items matching your search.'; -$GLOBALS['TL_LANG']['MSC']['details'] = 'Details'; -$GLOBALS['TL_LANG']['MSC']['field_label'] = '%s:'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50'][0] = 'w50'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50'][1] = - 'Set the field width to 50% and float it (float:left).'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50x'][0] = 'w50x'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50x'][1] = - 'Remove only the annoying fixed height, please use it together with "w50".'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clr'][0] = 'clr'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clr'][1] = 'Clear all floats.'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clx'][0] = 'clx'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clx'][1] = - 'Remove only the annoying overflow hidden, please use it together with "clr".'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['m12'][0] = 'm12'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['m12'][1] = - 'Add a 12 pixel top margin to the element (used for single checkboxes).'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['wizard'][0] = 'wizard'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['wizard'][1] = - 'Shorten the input field so there is enough room for the wizard button (e.g. date picker fields).'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['long'][0] = 'long'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['long'][1] = - 'Make the text input field span two columns.'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['search'][0] = 'Search'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['search'][1] = ''; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['sort'][0] = 'Sort'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['sort'][1] = ''; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['filter'][0] = 'Filter'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['filter'][1] = ''; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['limit'][0] = 'Limit'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['limit'][1] = ''; -$GLOBALS['TL_LANG']['ERR']['no_attribute_extension'] = - 'Please install at least one attribute extension! MetaModels without attributes do not make sense.'; -$GLOBALS['TL_LANG']['ERR']['activate_extension'] = - 'Please activate the required extension "%s" (%s)'; -$GLOBALS['TL_LANG']['ERR']['install_extension'] = - 'Please install the required extension "%s" (%s)'; -$GLOBALS['TL_LANG']['ERR']['columnExists'] = - 'There is already an attribute with the given column name.'; -$GLOBALS['TL_LANG']['ERR']['no_palette'] = - 'Attempt to access the MetaModel "%s" without input screen for current user %s.'; -$GLOBALS['TL_LANG']['ERR']['no_view'] = - 'Attempt to access the MetaModel "%s" without view for user %s.'; -$GLOBALS['TL_LANG']['ERR']['invalidTableName'] = 'The table name is invalid.'; -$GLOBALS['TL_LANG']['ERR']['upgrade_php_version'] = - 'The version of the PHP interpreter is too low, please upgrade to at least %s (you are currently running %s)'; -$GLOBALS['TL_LANG']['ERR']['invalidTableName'] = 'The table name "%s" is invalid.'; -$GLOBALS['TL_LANG']['ERR']['invalidColumnName'] = - 'The column name "%s" is invalid.'; -$GLOBALS['TL_LANG']['ERR']['systemColumn'] = - 'The column name "%s" is reserved for system use.'; -$GLOBALS['TL_LANG']['ERR']['tableDoesNotExist'] = 'Table "%s" does not exist.'; -$GLOBALS['TL_LANG']['ERR']['tableExists'] = 'Table "%s" already exists.'; -$GLOBALS['TL_LANG']['ERR']['columnDoesNotExist'] = - 'Column "%s" does not exist on table "%s".'; -$GLOBALS['TL_LANG']['ERR']['columnExists'] = - 'Column "%s" already exists on table "%s".'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['submit'] = 'Filter'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['do_not_filter'] = 'No filtering'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['select_all'] = 'Select all'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['clear_all'] = 'Clear all filter'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['action_add'] = '+'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['action_remove'] = '-'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['no_combinations'] = + +$GLOBALS['TL_LANG']['metamodels_frontendfilter']['submit'] = 'Filter'; +$GLOBALS['TL_LANG']['metamodels_frontendfilter']['do_not_filter'] = 'No filtering'; +$GLOBALS['TL_LANG']['metamodels_frontendfilter']['select_all'] = 'Select all'; +$GLOBALS['TL_LANG']['metamodels_frontendfilter']['clear_all'] = 'Clear all filter'; +$GLOBALS['TL_LANG']['metamodels_frontendfilter']['action_add'] = '+'; +$GLOBALS['TL_LANG']['metamodels_frontendfilter']['action_remove'] = '-'; +$GLOBALS['TL_LANG']['metamodels_frontendfilter']['no_combinations'] = ' (No matching combinations found.)'; -$GLOBALS['TL_LANG']['MSC']['metamodel_filtersettings_parameter']['simplelookup'][0] = 'Filter value for attribute "%s"'; -$GLOBALS['TL_LANG']['MSC']['metamodel_filtersettings_parameter']['simplelookup'][1] = ''; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['main_headline'] = 'We are calling for your help!'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['purpose'] = 'Purpose'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['other_donations'] = - 'For donations check also our - - fundraisings or consider - - donating for the manual.'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['contributor_headline'] = - 'Thanks to these users for tickets, suggestions and translations'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_name'][0] = 'MM'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_name'][1] = 'MetaModel'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_filter'][0] = 'Fi'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_filter'][1] = 'Filter'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_render_setting'][0] = 'Rs'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_render_setting'][1] = 'Rendersetting'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['contributor_link'] = 'See contributors at github...'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['main_text'] = - 'As the developers of this project, we receive no compensation for our work. - Much of our valuable time is freely given to the MetaModels project. Any - donations towards the MetaModels project would allow us to work on the - project during our working hours, speeding up development significantly. -

We guarantee to spend any donations only on this project. - If you have any specific requirements or features requests, you are free - to hire a member from our team, who will gladly - help you.

A big thanks to all our existing supporters and - sponsors. Please consider joining our list of sponsors. We could not - create such a popular open source project without the support of the - community.

More informations'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['help_headline'] = - 'Help for your MetaModels projects'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['help_text'] = - 'On our help - website as guide to MetaModels you can find the manual, all support - channels, videos tutorials and our newsletter service.

- - Direct link to the MetaModels manual...'; -$GLOBALS['TL_LANG']['METAMODELS_SYSTEM_COLUMNS']['id'] = 'ID'; -$GLOBALS['TL_LANG']['METAMODELS_SYSTEM_COLUMNS']['pid'] = 'PID'; -$GLOBALS['TL_LANG']['METAMODELS_SYSTEM_COLUMNS']['sorting'] = 'Sorting'; -$GLOBALS['TL_LANG']['METAMODELS_SYSTEM_COLUMNS']['tstamp'] = 'Timestamp'; -$GLOBALS['TL_LANG']['METAMODELS_SYSTEM_COLUMNS']['vargroup'] = 'Vargroup'; -$GLOBALS['TL_LANG']['METAMODELS_SYSTEM_COLUMNS']['varbase'] = 'Varbase'; -$GLOBALS['TL_LANG']['METAMODELS_SYSTEM_COLUMNS']['meta'] = 'Metafields'; -$GLOBALS['TL_LANG']['METAMODELS_SYSTEM_COLUMNS']['attributes'] = 'Attributes'; diff --git a/src/CoreBundle/Resources/contao/languages/en/explain.php b/src/CoreBundle/Resources/contao/languages/en/explain.php old mode 100644 new mode 100755 diff --git a/src/CoreBundle/Resources/contao/languages/en/modules.php b/src/CoreBundle/Resources/contao/languages/en/modules.php old mode 100644 new mode 100755 index 7f8c7fcbe..38e3500c7 --- a/src/CoreBundle/Resources/contao/languages/en/modules.php +++ b/src/CoreBundle/Resources/contao/languages/en/modules.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2018 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @subpackage Core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2018 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,11 +23,6 @@ /** * Back end modules */ -$GLOBALS['TL_LANG']['MOD']['metamodels'][0] = 'MetaModels'; -$GLOBALS['TL_LANG']['MOD']['metamodels'][1] = - 'The MetaModels extension allows you to create own data models.'; -$GLOBALS['TL_LANG']['MOD']['support_metamodels'][0] = 'Support MetaModels'; -$GLOBALS['TL_LANG']['MOD']['support_metamodels'][1] = 'Support information for the MetaModels extension.'; $GLOBALS['TL_LANG']['FMD']['metamodels'][0] = 'MetaModels'; $GLOBALS['TL_LANG']['FMD']['metamodels'][1] = 'The MetaModels extension allows you to create own data models.'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_content.php b/src/CoreBundle/Resources/contao/languages/en/tl_content.php old mode 100644 new mode 100755 index e697426a1..5ca272516 --- a/src/CoreBundle/Resources/contao/languages/en/tl_content.php +++ b/src/CoreBundle/Resources/contao/languages/en/tl_content.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Christian Schiffler * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -42,7 +42,7 @@ 'Please specify the offset value (i.e. 10 to skip the first 10 items).'; $GLOBALS['TL_LANG']['tl_content']['metamodel_limit'][0] = 'Maximum number of items'; $GLOBALS['TL_LANG']['tl_content']['metamodel_limit'][1] = - 'Please enter the maximum number of items. Enter 0 to show all items and therefore disable the pagination.'; + 'Please enter the maximum number of items - enter 0 to show all items.'; $GLOBALS['TL_LANG']['tl_content']['metamodel_sortby'][0] = 'Order by'; $GLOBALS['TL_LANG']['tl_content']['metamodel_sortby'][1] = 'Please choose the sort order.'; @@ -64,6 +64,9 @@ $GLOBALS['TL_LANG']['tl_content']['metamodel_order_dir_param'][0] = 'Override key for order direction'; $GLOBALS['TL_LANG']['tl_content']['metamodel_order_dir_param'][1] = 'You can override the default key \'orderDir\'.'; +$GLOBALS['TL_LANG']['tl_content']['metamodel_sort_urlfragment'][0] = 'URL fragment'; +$GLOBALS['TL_LANG']['tl_content']['metamodel_sort_urlfragment'][1] = + 'Add URL fragment to jump to anchor or id.'; $GLOBALS['TL_LANG']['tl_content']['metamodel_filtering'][0] = 'Filter settings to apply'; $GLOBALS['TL_LANG']['tl_content']['metamodel_filtering'][1] = 'Select the filter settings that shall get applied when compiling the list.'; @@ -84,9 +87,9 @@ $GLOBALS['TL_LANG']['tl_content']['metamodel_page_param_type'][0] = 'URL-Type for pagination'; $GLOBALS['TL_LANG']['tl_content']['metamodel_page_param_type'][1] = 'Please specify the type of URL parameters als slug (key/value) or GET (key=value).'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_maxpaginationlinks'][0] = +$GLOBALS['TL_LANG']['tl_content']['metamodel_maxpaginationlinks'][0] = 'Maximum number of pagination links'; -$GLOBALS['TL_LANG']['tl_content']['metamodel_maxpaginationlinks'][1] = +$GLOBALS['TL_LANG']['tl_content']['metamodel_maxpaginationlinks'][1] = 'Please enter the maximum number pagination links. Enter 0 to show the default value from Contao e.g. 7.'; $GLOBALS['TL_LANG']['tl_content']['metamodel_pagination'][0] = 'Custom template to use for pagination'; @@ -109,6 +112,10 @@ $GLOBALS['TL_LANG']['tl_content']['metamodel_jumpTo'][0] = 'Redirect page'; $GLOBALS['TL_LANG']['tl_content']['metamodel_jumpTo'][1] = 'Please choose the page to which visitors will be redirected when clicking a link or submitting a form.'; +$GLOBALS['TL_LANG']['tl_content']['metamodel_fef_id'][0] = 'Form ID'; +$GLOBALS['TL_LANG']['tl_content']['metamodel_fef_id'][1] = + 'Set ID as own postfix for "FORM_SUBMIT" value e.g. to use redirect and another filter -' . + ' in which case the value must be the same. '; $GLOBALS['TL_LANG']['tl_content']['metamodel_fef_urlfragment'][0] = 'URL fragment'; $GLOBALS['TL_LANG']['tl_content']['metamodel_fef_urlfragment'][1] = 'Add URL fragment to jump to anchor or id.'; @@ -147,11 +154,3 @@ $GLOBALS['TL_LANG']['tl_content']['metamodel_meta_description'][0] = 'Meta Description'; $GLOBALS['TL_LANG']['tl_content']['metamodel_meta_description'][1] = 'Set this attribute as the meta-description of the page.'; -$GLOBALS['TL_LANG']['tl_content']['editmetamodel'][0] = 'Edit MetaModel'; -$GLOBALS['TL_LANG']['tl_content']['editmetamodel'][1] = 'Edit the MetaModel ID %s.'; -$GLOBALS['TL_LANG']['tl_content']['editrendersetting'][0] = 'Edit rendersetting'; -$GLOBALS['TL_LANG']['tl_content']['editrendersetting'][1] = - 'Edit the render setting ID %s.'; -$GLOBALS['TL_LANG']['tl_content']['editfiltersetting'][0] = 'Edit filtersetting'; -$GLOBALS['TL_LANG']['tl_content']['editfiltersetting'][1] = - 'Edit the filter setting ID %s.'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_form_field.php b/src/CoreBundle/Resources/contao/languages/en/tl_form_field.php old mode 100644 new mode 100755 diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_maintenance.php b/src/CoreBundle/Resources/contao/languages/en/tl_maintenance.php old mode 100644 new mode 100755 diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel.php deleted file mode 100644 index e8214ccf3..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel.php +++ /dev/null @@ -1,86 +0,0 @@ - - * @author Christian de la Haye - * @author Sven Baumann - * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel']['name'][0] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel']['name'][1] = 'MetaModel name.'; -$GLOBALS['TL_LANG']['tl_metamodel']['tstamp'][0] = 'Revision date'; -$GLOBALS['TL_LANG']['tl_metamodel']['tstamp'][1] = 'Date and time of the latest revision'; -$GLOBALS['TL_LANG']['tl_metamodel']['tableName'][0] = 'Table name'; -$GLOBALS['TL_LANG']['tl_metamodel']['tableName'][1] = 'Name of database table to store items to.'; -$GLOBALS['TL_LANG']['tl_metamodel']['mode'][0] = 'Parent list mode'; -$GLOBALS['TL_LANG']['tl_metamodel']['mode'][1] = 'Mode to use for parent/child relationship.'; -$GLOBALS['TL_LANG']['tl_metamodel']['translated'][0] = 'Translation'; -$GLOBALS['TL_LANG']['tl_metamodel']['translated'][1] = - 'Check if this MetaModel shall support translation/multilingualism.'; -$GLOBALS['TL_LANG']['tl_metamodel']['languages'][0] = 'Languages to provide for translation'; -$GLOBALS['TL_LANG']['tl_metamodel']['languages'][1] = - 'Specify all languages that shall be available for translation.'; -$GLOBALS['TL_LANG']['tl_metamodel']['languages_langcode'][0] = 'Language'; -$GLOBALS['TL_LANG']['tl_metamodel']['languages_langcode'][1] = 'Select the languages you want to provide.'; -$GLOBALS['TL_LANG']['tl_metamodel']['languages_isfallback'][0] = 'Fallback language'; -$GLOBALS['TL_LANG']['tl_metamodel']['languages_isfallback'][1] = 'Check the language that shall be used as fallback.'; -$GLOBALS['TL_LANG']['tl_metamodel']['varsupport'][0] = 'Variant support'; -$GLOBALS['TL_LANG']['tl_metamodel']['varsupport'][1] = - 'Check if this MetaModel shall support variants of items.'; -$GLOBALS['TL_LANG']['tl_metamodel']['localeterritorysupport'][0] = 'Locale territory support'; -$GLOBALS['TL_LANG']['tl_metamodel']['localeterritorysupport'][1] = - 'Check if this MetaModel shall support language territory at locale.'; -$GLOBALS['TL_LANG']['tl_metamodel']['sorting'][0] = 'Sorting'; -$GLOBALS['TL_LANG']['tl_metamodel']['sorting'][1] = 'Sorting order of items.'; -$GLOBALS['TL_LANG']['tl_metamodel']['title_legend'] = 'Name and table'; -$GLOBALS['TL_LANG']['tl_metamodel']['translated_legend'] = 'Translation'; -$GLOBALS['TL_LANG']['tl_metamodel']['advanced_legend'] = 'Advanced settings'; -$GLOBALS['TL_LANG']['tl_metamodel']['new'][0] = 'New MetaModel'; -$GLOBALS['TL_LANG']['tl_metamodel']['new'][1] = 'Create a new MetaModel.'; -$GLOBALS['TL_LANG']['tl_metamodel']['edit'][0] = 'Manage items'; -$GLOBALS['TL_LANG']['tl_metamodel']['edit'][1] = 'Manage items of MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['copy'][0] = 'Copy MetaModel definition'; -$GLOBALS['TL_LANG']['tl_metamodel']['copy'][1] = 'Copy definition of MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['delete'][0] = 'Delete MetaModel'; -$GLOBALS['TL_LANG']['tl_metamodel']['delete'][1] = 'Delete MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['show'][0] = 'MetaModel details'; -$GLOBALS['TL_LANG']['tl_metamodel']['show'][1] = 'Show details of MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['editheader'][0] = 'Edit MetaModel'; -$GLOBALS['TL_LANG']['tl_metamodel']['editheader'][1] = 'Edit the MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['fields'][0] = 'Define attributes'; -$GLOBALS['TL_LANG']['tl_metamodel']['fields'][1] = 'Define attributes for MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['filter'][0] = 'Define filters'; -$GLOBALS['TL_LANG']['tl_metamodel']['filter'][1] = 'Define filters for MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['rendersettings'][0] = 'Define render settings'; -$GLOBALS['TL_LANG']['tl_metamodel']['rendersettings'][1] = 'Define render settings for MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['dca'][0] = 'Define input screens'; -$GLOBALS['TL_LANG']['tl_metamodel']['dca'][1] = 'Define input screens for MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['dca_combine'][0] = 'Define input/output combinations'; -$GLOBALS['TL_LANG']['tl_metamodel']['dca_combine'][1] = - 'Define input/output combinations for MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['cut'][0] = 'Move MetaModel'; -$GLOBALS['TL_LANG']['tl_metamodel']['cut'][1] = 'Define the order of your MetaModels.'; -$GLOBALS['TL_LANG']['tl_metamodel']['searchable_pages'][0] = 'Define search settings'; -$GLOBALS['TL_LANG']['tl_metamodel']['searchable_pages'][1] = 'Define search settings for MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['pastenew'][0] = 'Add new at the top'; -$GLOBALS['TL_LANG']['tl_metamodel']['pastenew'][1] = 'Add new after MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['pasteafter'][0] = 'Create new MetaModel'; -$GLOBALS['TL_LANG']['tl_metamodel']['pasteafter'][1] = 'Create new after MetaModel ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel']['itemFormatCount']['0'] = '%s items'; -$GLOBALS['TL_LANG']['tl_metamodel']['itemFormatCount']['1'] = '%s item'; -$GLOBALS['TL_LANG']['tl_metamodel']['itemFormatCount']['2:'] = '%s items'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_attribute.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_attribute.php deleted file mode 100644 index 4de4d0716..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_attribute.php +++ /dev/null @@ -1,66 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2018 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['type'][0] = 'Attribute type'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['type'][1] = - 'Select the type of this attribute. ' . - 'WARNING: if you change this, all existing data within this attribute will be deleted.'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['name'][0] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['name'][1] = 'Human readable name'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['description'][0] = 'Description'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['description'][1] = 'Human readable description'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['colname'][0] = 'Column name'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['colname'][1] = - 'Internal reference name for this attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['isvariant'][0] = 'Enable variant override'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['isvariant'][1] = - 'Check this, if you want variants within the MetaModel to override the parent item\'s value'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['isunique'][0] = 'Unique values'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['isunique'][1] = - 'Check this, if you want to ensure that each value only occurs once'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['name_langcode'] = 'Language'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['name_value'] = 'Description'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['title_legend'] = - 'Type, naming and base attribute configuration'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['advanced_legend'] = 'Advanced settings'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['new'][0] = 'New attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['new'][1] = 'Create new attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['pastenew'][0] = 'New attribute after this Attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['pastenew'][1] = 'New attribute after this Attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['edit'][0] = 'Edit attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['edit'][1] = 'Edit attribute ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['cut'][0] = 'Cut attribute definition'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['cut'][1] = 'Cut definition of attribute ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['copy'][0] = 'Copy attribute definition'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['copy'][1] = 'Copy definition of attribute ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['delete'][0] = 'Delete attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['delete'][1] = 'Delete attribute ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['show'][0] = 'Attribute details'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['show'][1] = 'Show details of attribute ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['editheader'][0] = 'Edit attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['editheader'][1] = 'Edit the attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['pastenew'][0] = 'Add new at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['pastenew'][1] = 'Add new after attribute ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['pasteafter'][0] = 'Create new attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['pasteafter'][1] = 'Create new after attribute ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['error_unknown_attribute'][0] = 'Unknown attribute!'; -$GLOBALS['TL_LANG']['tl_metamodel_attribute']['error_unknown_attribute'][1] = - 'Extension missing? The attribute type "%s" is not installed.'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dca.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dca.php deleted file mode 100644 index 566c92f8f..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dca.php +++ /dev/null @@ -1,97 +0,0 @@ - - * @author Ingolf Steinhardt - * @author Sven Baumann - * @copyright 2012-2020 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel_dca']['name'][0] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['name'][1] = 'Name of the input screen.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['tstamp'][0] = 'Revision date'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['tstamp'][1] = 'Date and time of the latest revision.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendertype'][0] = 'Integration'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendertype'][1] = 'Select the desired type of integration.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendermode'][0] = 'Render mode'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendermode'][1] = 'Select the desired render mode.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['ptable'][0] = 'Parent table name'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['ptable'][1] = - 'Name of the database table that shall be referred to as parent table.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['showColumns'][0] = 'Use column based layout'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['showColumns'][1] = - 'If selected a table header will be generated with column names.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['backendsection'][0] = 'Backend section'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['backendsection'][1] = - 'Select the desired backend section where you want the MetaModel appear. ' . - 'For models that shall be edited by end users, the "content" section most likely will be appropriate.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['backendicon'][0] = 'Backend icon'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['backendicon'][1] = - 'Select the desired backend icon.' . - 'This icon will get used to draw an image in the parent list if you have a integration as child table.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['backendcaption'][0] = 'Backend caption'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['backendcaption'][1] = - 'The text you specify in here, will get used as the label and description text in the backend menu.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['becap_langcode'][0] = 'Language'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['becap_langcode'][1] = 'Select the languages you want to provide.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['becap_label'][0] = 'Label text'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['becap_label'][1] = - 'The text you specify in here, will get used as the menu label in the backend menu.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['becap_description'][0] = 'Description text'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['becap_description'][1] = - 'The text you specify in here, will get used as the description (hover title) in the backend menu.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['panelLayout'][0] = 'Panel layout'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['panelLayout'][1] = - 'Separate panel options with comma (= space) and semicolon (= new line) like "filter;search;sort,limit".'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['panelpicker'] = 'Panelpicker'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['use_limitview'][0] = 'View limitation'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['use_limitview'][1] = 'Activate the view limitation.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['limit_rendersetting'][0] = 'Limit the render setting'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['limit_rendersetting'][1] = 'Choose between front end or backend.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['iseditable'][0] = 'Allow editing of items'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['iseditable'][1] = - 'If checked, this input screen allows the editing of items.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['iscreatable'][0] = 'Allow creating of items'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['iscreatable'][1] = - 'If checked, this input screen allows the creating of items.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['isdeleteable'][0] = 'Allow deleting of items'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['isdeleteable'][1] = - 'If checked, this input screen allows the deleting of items.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['title_legend'] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['view_legend'] = 'View settings'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['backend_legend'] = 'Backend integration'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['display_legend'] = 'Data display settings'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['permissions_legend'] = 'Data manipulation permissions'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['new'][0] = 'New input screen'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['new'][1] = 'Create new input screen'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['edit'][0] = 'Edit input screen'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['edit'][1] = 'Edit the input screen ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['copy'][0] = 'Copy input screen definition'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['copy'][1] = 'Copy definition of input screen ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['delete'][0] = 'Delete input screen'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['delete'][1] = 'Delete input screen ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['show'][0] = 'Input screen details'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['show'][1] = 'Show details of input screen ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['settings'][0] = 'Input screen settings'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['settings'][1] = 'Edit the settings of input screen ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['groupsort_settings'][0] = 'Grouping and sorting'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['groupsort_settings'][1] = - 'Edit the grouping and sorting settings of input screen ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendertypes']['standalone'] = 'Standalone'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendertypes']['ctable'] = 'As child table'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendermodes']['flat'] = 'Flat'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendermodes']['parented'] = 'Parented'; -$GLOBALS['TL_LANG']['tl_metamodel_dca']['rendermodes']['hierarchical'] = 'Hierarchical'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dca_combine.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dca_combine.php deleted file mode 100644 index 706149da0..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dca_combine.php +++ /dev/null @@ -1,38 +0,0 @@ - - * @author Sven Baumann - * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['dca_combiner'][0] = 'Permissions for input screen and views'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['dca_combiner'][1] = - 'For selected frontend user group (if any) and selected backend user group (if any) use the selected palette and ' . - 'the selected view.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['fe_group'][0] = 'Frontend group'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['fe_group'][1] = - 'The frontend user group the combination applies to; * is \'catch all\'.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['be_group'][0] = 'Backend group'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['be_group'][1] = - 'The backend user group the combination applies to; * is \'catch all\'.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['dca_id'][0] = 'The input screen'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['dca_id'][1] = 'The input screen the combination applies to.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['view_id'][0] = 'The render setting'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['view_id'][1] = 'The view the combination applies to.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['sysadmin'] = 'Administrator'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_combine']['anonymous'] = 'Anonymous'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dca_sortgroup.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dca_sortgroup.php deleted file mode 100644 index 7a2e101ec..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dca_sortgroup.php +++ /dev/null @@ -1,84 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2018 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -/** - * Fields - */ -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['name'][0] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['name'][1] = 'Name of the sorting group.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['tstamp'][0] = 'Revision date'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['tstamp'][1] = - 'Date and time of the latest revision.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['isdefault'][0] = 'Is default'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['isdefault'][1] = - 'Determines that this input screen shall be used as default for the parenting MetaModel.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendermode'][0] = 'Render mode'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendermode'][1] = 'Select the desired render mode.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['becap_langcode'][0] = 'Language'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['becap_langcode'][1] = - 'Select the languages you want to provide.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['becap_label'][0] = 'Label text'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['becap_label'][1] = - 'The text you specify in here, will get used as the menu label in the backend menu.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['becap_description'][0] = 'Description text'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['becap_description'][1] = - 'The text you specify in here, will get used as the description (hover title) in the backend menu.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptype'][0] = 'Grouping type'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptype'][1] = - 'The grouping type to use in the item view.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergroupattr'][0] = 'Grouping attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergroupattr'][1] = - 'The attribute to use for grouping in the item view.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouplen'][0] = 'Grouping length'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouplen'][1] = - 'The amount of characters to use for grouping.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendersortattr'][0] = 'Sorting attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendersortattr'][1] = 'The attribute to sort by.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendersort'][0] = 'Sorting direction'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendersort'][1] = 'The sorting direction.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['ismanualsort'][0] = 'Enable manual sorting'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['ismanualsort'][1] = - 'If this is enabled, the user will be able to perform manual sorting.'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['title_legend'] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['display_legend'] = 'Data display settings'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['new'][0] = 'New definition'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['new'][1] = 'Create new definition'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['edit'][0] = 'Edit definition'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['edit'][1] = 'Edit the definition ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['copy'][0] = 'Copy definition'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['copy'][1] = 'Copy definition ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['delete'][0] = 'Delete definition'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['delete'][1] = 'Delete definition ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['show'][0] = 'Definition details'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['show'][1] = 'Show details of definition ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['settings'][0] = 'Definition settings'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['settings'][1] = - 'Edit the settings of definition ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptypes']['none'] = 'Do not group '; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptypes']['char'] = 'Group by initial letter(s)'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptypes']['digit'] = 'Group by numeric order'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptypes']['day'] = 'Group by day of date'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptypes']['weekday'] = 'Group by weekday of date'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptypes']['week'] = 'Group by week of year'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptypes']['month'] = 'Group by month of date'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendergrouptypes']['year'] = 'Group by year of date'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendersortdirections']['asc'] = 'Ascending'; -$GLOBALS['TL_LANG']['tl_metamodel_dca_sortgroup']['rendersortdirections']['desc'] = 'Descending'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dcasetting.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dcasetting.php deleted file mode 100644 index 7c69c2eb7..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dcasetting.php +++ /dev/null @@ -1,140 +0,0 @@ - - * @author Cliff Parnitzky - * @author Ingolf Steinhardt - * @author Sven Baumann - * @copyright 2012-2022 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -/** - * Fields - */ - -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['dcatype'][0] = 'Type'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['dcatype'][1] = 'Select the attribute type.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['attr_id'][0] = 'Attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['attr_id'][1] = 'Attribute this setting relates to.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['template'][0] = 'Custom template to use for generating'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['template'][1] = - 'Select the template that shall be used for the selected attribute. ' . - 'Valid template files start with "mm_<type>" where the type name is put for <type>'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['tl_class'][0] = 'Backend class'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['tl_class'][1] = - 'Here you can set backend class(es). Open the wizard for an overview of the classes.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['legendhide'][0] = 'Collapse section'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['legendhide'][1] = 'Collapse the section by default.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['legendtitle'][0] = 'Legend title'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['legendtitle'][1] = 'Here you can enter the legend title.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['name_langcode'] = 'Language'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['name_value'] = 'Legend title'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['mandatory'][0] = 'Mandatory'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['mandatory'][1] = 'Check if this attribute shall be ' . - 'mandatory. -
NOTE: This will be implicitely active on if you selected "Unique values" in the attribute configuration.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['alwaysSave'][0] = 'Always save'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['alwaysSave'][1] = - 'If true the field will always be saved, even if its value has not changed.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['chosen'][0] = 'Chosen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['chosen'][1] = 'Enable Chosen graphical select widget.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['filterable'][0] = 'Filterable'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['filterable'][1] = - 'Check if this attribute shall be available for backend filtering.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['searchable'][0] = 'Searchable'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['searchable'][1] = - 'Check if this attribute shall be available for backend search.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['allowHtml'][0] = 'Do not encode allowed html tags.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['allowHtml'][1] = - 'If you select this, allowed HTML tags from system settings will not be encoded.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['preserveTags'][0] = 'Do not encode all html tags.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['preserveTags'][1] = - 'If you select this, no HTML tags will be encoded.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['decodeEntities'][0] = 'Decode HTML entities.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['decodeEntities'][1] = - 'If you select this, all HTML entities will be decoded. Note that HTML entities are always decoded if "Do not encode allowed html tags" is true.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rte'][0] = 'Enable richtext editor on this'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rte'][1] = - 'Select the rich text configuration that shall be used on this field (if any).'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rows'][0] = 'Rows'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rows'][1] = - 'Amount of rows to use for longtext/table widget.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['cols'][0] = 'Columns'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['cols'][1] = - 'Amount of colums to use for longtext/table widget'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash'][0] = 'Trailing slash handling'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash'][1] = - 'Here you can specify how trailing slashes shall be handled'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['spaceToUnderscore'][0] = 'Replace spaces with underscore'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['spaceToUnderscore'][1] = - 'If true any whitespace character will be replaced by an underscore.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['includeBlankOption'][0] = 'Include blank option'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['includeBlankOption'][1] = - 'if true a blank option will be added to the options which allows to define a "no item selected" option.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['readonly'][0] = 'Read only'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['readonly'][1] = - 'If true a the widget will be read only and may not be changed.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['submitOnChange'][0] = 'Submit on change'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['submitOnChange'][1] = - 'If active the form will be submitted when the field value changes.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['title_legend'] = 'Type'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['presentation_legend'] = 'Widget appearance related options'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['functions_legend'] = 'Functionality related options'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['overview_legend'] = - 'Filtering and searching in the backend list'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['backend_legend'] = 'Backend'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['config_legend'] = 'Configuration'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['advanced_legend'] = 'Advanced'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['new'][0] = 'New'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['new'][1] = 'Create new setting.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['edit'][0] = 'Edit setting'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['edit'][1] = 'Edit setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['cut'][0] = 'Cut setting definition'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['cut'][1] = 'Cut setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['copy'][0] = 'Copy setting definition'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['copy'][1] = 'Copy setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['delete'][0] = 'Delete setting'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['delete'][1] = 'Delete setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['conditions'][0] = 'Manage visibility conditions'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['conditions'][1] = - 'Manage the visibility conditions of property ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['show'][0] = 'Setting details'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['show'][1] = 'Show details of setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['toggle'][0] = 'Toggle'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['toggle'][1] = 'Toggle the state of setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['addall'][0] = 'Add all'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['addall'][1] = 'Add all attributes to input screen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pastenew'][0] = 'Add new at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pastenew'][1] = 'Add new after setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pasteafter'][0] = 'Create new setting at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pasteafter'][1] = 'Create new after setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['dcatypes']['legend'] = 'Legend'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['dcatypes']['attribute'] = 'Attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash_options']['0'] = 'Strip slash on save'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash_options']['1'] = 'Add slash on save'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash_options']['2'] = 'Do nothing'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['addAll_willadd'] = - 'Will add the attribute "%s" [%s, "%s"] to the input screen.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['addAll_alreadycontained'] = - 'Attribute "%s" [%s, "%s"] is already contained in input screen.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['addAll_addsuccess'] = - 'Added the attribute "%s" [%s, "%s"] to the input screen.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['addAll_activate'] = 'Add new settings enabled.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['addAll_tlclass'] = 'Set backend class.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['mandatory_for_unique_attr'] = - 'Unique attribues are automatically mandatory (this is not changable).'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['readonly_for_force_alias'] = - 'Attribues with force alias are automatically readonly (this is not changable).'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dcasetting_condition.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dcasetting_condition.php deleted file mode 100644 index 2ef0a06b6..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_dcasetting_condition.php +++ /dev/null @@ -1,106 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2020 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -/** - * Legends - */ -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['list_label'] = - 'Visibility conditons'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['basic_legend'] = - 'Basic configuration'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['config_legend'] = - 'Condition configuration'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['type'][0] = 'Type'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['type'][1] = - 'Select the condition type.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['enabled'][0] = 'Enabled'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['enabled'][1] = - 'Check to enable this condition.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['comment'][0] = 'Comment'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['comment'][1] = - 'Enter a comment to describe the purpose of this condition.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['attr_id'][0] = - 'Attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['attr_id'][1] = - 'Select the attribute to use for this condition.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['value'][0] = 'Value'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['value'][1] = - 'Please select the desired value.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['new'][0] = 'New'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['new'][1] = - 'Create new setting.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['edit'][0] = - 'Edit setting'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['edit'][1] = - 'Edit setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['cut'][0] = - 'Cut setting definition'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['cut'][1] = - 'Cut setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['copy'][0] = - 'Copy setting definition'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['copy'][1] = - 'Copy setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['delete'][0] = - 'Delete setting'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['delete'][1] = - 'Delete setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['conditions'][0] = - 'Manage conditions'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['conditions'][1] = - 'Manage the conditions of property ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['show'][0] = - 'Setting details'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['show'][1] = - 'Show details of setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['toggle'][0] = 'Toggle'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['toggle'][1] = - 'Toggle the state of setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['addall'][0] = 'Add all'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['addall'][1] = - 'Add all attributes to input screen'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['pastenew'][0] = - 'Add new at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['pastenew'][1] = - 'Add new after setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['pasteafter'][1] = - 'Create new after setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['pasteinto'][0] = - 'Create new setting at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['pasteinto'][1] = - 'Create new at the top of setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['conditionnames']['conditionpropertyvalueis'] = - 'Attribute value is...'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['conditionnames']['conditionpropertycontainanyof'] = - 'Attribute values contain any of...'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['conditionnames']['conditionpropertyvisible'] = - 'Is attribute visible...'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['conditionnames']['conditionor'] = 'OR'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['conditionnames']['conditionand'] = 'AND'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['conditionnames']['conditionnot'] = 'NOT'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['typedesc']['_default_'] = - '%s %s
for attribute %s (Parameter: %s)'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['typedesc']['conditionor'] = - '%s %s
any sub conditions must be fulfilled'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['typedesc']['conditionand'] = - '%s %s
all sub conditions must be fulfilled'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['typedesc']['conditionnot'] = - '%s %s
invert the result of the contained condition'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_filter.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_filter.php deleted file mode 100644 index d0ab64afb..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_filter.php +++ /dev/null @@ -1,42 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2018 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel_filter']['name'][0] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['name'][1] = 'Filter setting name.'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['tstamp'][0] = 'Revision date'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['tstamp'][1] = 'Date and time of the latest revision.'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['title_legend'] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['pastenew'][0] = 'New'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['pastenew'][1] = 'Create a new filter setting'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['editheader'][0] = 'Edit'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['editheader'][1] = 'Edit MetaModel'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['new'][0] = 'New'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['new'][1] = 'Create new filter setting'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['edit'][0] = 'Edit setting'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['edit'][1] = 'Edit filter setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['copy'][0] = 'Copy filter setting'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['copy'][1] = 'Copy filter setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['delete'][0] = 'Delete filter setting'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['delete'][1] = 'Delete filter setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['show'][0] = 'Filter setting details'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['show'][1] = 'Show details of filter setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['settings'][0] = 'Define attribute settings'; -$GLOBALS['TL_LANG']['tl_metamodel_filter']['settings'][1] = 'Define attribute settings for filter setting ID %s'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_filtersetting.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_filtersetting.php deleted file mode 100644 index b9ec9c907..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_filtersetting.php +++ /dev/null @@ -1,154 +0,0 @@ - - * @author Sven Baumann - * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -/** - * Fields - */ -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['list_label'] = 'Filter settings'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['fid'][0] = 'Parent collection'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['fid'][1] = - 'The collection of filter settings, this setting belongs to.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['type'][0] = 'Type'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['type'][1] = 'The type of this setting.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['enabled'][0] = 'Enabled'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['enabled'][1] = 'Enable this filter setting.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['comment'][0] = 'Comment'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['comment'][1] = - 'A short comment for describing the purpose of this filter setting.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['attr_id'][0] = 'Attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['attr_id'][1] = - 'Attribute this setting relates to.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['all_langs'][0] = 'Search all languages'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['all_langs'][1] = - 'Check if you want to perform the lookup language independant. ' . - 'If this is not checked, only the current active language will be searched.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['allow_empty'][0] = 'Allow empty value'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['allow_empty'][1] = - 'Check if you want to allow this filter value to be emtpy, if checked and the parameter holds an empty value, ' . - 'this filter rule will behave as if it was not defined.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['stop_after_match'][0] = 'Stop after first match'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['stop_after_match'][1] = - 'Check if you want this filter setting to stop executing its child rules after the first subset returned matches.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['urlparam'][0] = 'URL parameter'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['urlparam'][1] = - 'The URL parameter that shall get mapped to the selected attribute. ' . - 'The special "auto_item" parameter can also be used, this is especially useful for alias columns.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['predef_param'][0] = 'Static parameter'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['predef_param'][1] = - 'Check if you want to be able to set the value of this parameter in the parenting list ' . - '(modules, content elements, etc.).'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['fe_widget'][0] = 'Provide Frontend widget'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['fe_widget'][1] = - 'Check if you want to display a filter widget in the Frontend.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['customsql'][0] = 'Custom SQL Query'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['customsql'][1] = - 'The SQL query that shall be executed, insert tags are supported.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['label'][0] = 'Label'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['label'][1] = - 'Show label instead of attribute name.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['template'][0] = 'Template'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['template'][1] = - 'Sub template for this filter element. Standard: form widget.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['blankoption'][0] = 'Empty option'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['blankoption'][1] = - 'Show empty options in filter widget.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['onlyused'][0] = 'Assigned values only'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['onlyused'][1] = - 'Show only options, that are assigned somewhere in the MetaModel.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['onlypossible'][0] = 'Remaining values only'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['onlypossible'][1] = - 'Show only options, that are still assigned somewhere after the actual filter is set.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['skipfilteroptions'][0] = - 'Ignore this filter for the remaining values'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['skipfilteroptions'][1] = - 'If activate the filter will return all options without itself in the filter rules.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['hide_label'][0] = - 'Hide label in filter widget'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['hide_label'][1] = - 'If active, the label is not output.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['label_as_blankoption'][0] = - 'Use label as blank option'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['label_as_blankoption'][1] = - 'If active, the label output as blank option.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['apply_sorting'][0] = 'Sorting'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['apply_sorting'][1] = - 'Please enter sorting of values in filter widget.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['cssID'][0] = 'CSS ID/class'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['cssID'][1] = - 'Here you can set an ID and one or more classes.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['placeholder'][0] = 'Placeholder'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['placeholder'][1] = - 'Show this text as long as the field is empty.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['sorting_directions']['natsort_asc'] = 'Natural sorting (ASC)'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['sorting_directions']['natsort_desc'] = 'Natural sorting (DESC)'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['defaultid'][0] = 'Default'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['defaultid'][1] = - 'Default value for selection.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['title_legend'] = 'Type'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['config_legend'] = 'Configuration'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['fefilter_legend'] = 'Frontend filter'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['new'][0] = 'New'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['new'][1] = 'Create new setting.'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['edit'][0] = 'Edit setting'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['edit'][1] = 'Edit filter setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['copy'][0] = - 'Copy filter setting definition'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['copy'][1] = 'Copy filter setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['cut'][0] = - 'Cut filter setting definition'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['cut'][1] = 'Cut filter setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['delete'][0] = 'Delete filter setting'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['delete'][1] = 'Delete filter setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['show'][0] = 'Filter setting details'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['show'][1] = - 'Show details of filter setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['toggle'][0] = 'Toggle'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['toggle'][1] = 'Toggle the state of setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['pastenew'][0] = 'Add new at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['pastenew'][1] = 'Add new after setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['pasteafter'][1] = - 'Create new after setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['pasteinto'][0] = - 'Create new setting at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['pasteinto'][1] = - 'Create new at the top of setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typenames']['idlist'] = 'Predefined set of items'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typenames']['simplelookup'] = 'Simple lookup'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typenames']['customsql'] = 'Custom SQL'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typenames']['conditionor'] = 'OR condition'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typenames']['conditionand'] = 'AND condition'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['_attribute_'] = ' [%s, "%s"]'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['_url_'] = ' (URL: %s)'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['_comment_'] = '
%s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['_default_'] = - '%1$s %2$s%3$s%5$s%4$s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['simplelookup'] = - '%1$s %2$s%3$s%5$s%4$s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['fefilter'] = - '%1$s %2$s %3$s%4$s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['conditionor'] = - '%1$s %2$s%4$s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['typedesc']['conditionand'] = - '%1$s %2$s%4$s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['items'][0] = 'Items'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['items'][1] = - 'Please enter the IDs of the items for filtering as comma-separated list.'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_item.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_item.php deleted file mode 100644 index 63a35724c..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_item.php +++ /dev/null @@ -1,50 +0,0 @@ - - * @author Sven Baumann - * @copyright 2012-2018 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel_item']['new'][0] = 'New item'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['new'][1] = 'Create new item'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['edit'][0] = 'Edit item'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['edit'][1] = 'Edit item ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['copy'][0] = 'Copy item'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['copy'][1] = 'Copy item ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['createvariant'][0] = 'New variant'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['createvariant'][1] = 'Create a new variant of item ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['cut'][0] = 'Move item'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['cut'][1] = 'Move item ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['delete'][0] = 'Delete item'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['delete'][1] = 'Delete item ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['show'][0] = 'Item details'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['show'][1] = 'Show details of item ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['editheader'][0] = 'Edit item type'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['editheader'][1] = 'Edit the item type'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['pasteafter'][0] = 'Create new item'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['pasteafter'][1] = 'Create a new item after item ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['pasteinto'][0] = 'Create new item'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['pasteinto'][1] = 'Create a new item in item ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['pastenew'][0] = 'Add new at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['pastenew'][1] = 'Add new item after item ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['varbase'][0] = 'Is variant base'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['varbase'][1] = - 'Check this if you want to make this the base for the current variant group'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['editRecord'] = 'Edit item %s'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['newRecord'] = 'Create a new item'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['sorting'][0] = 'Sorting'; -$GLOBALS['TL_LANG']['tl_metamodel_item']['sorting'][1] = 'The manual sorting'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_rendersetting.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_rendersetting.php deleted file mode 100644 index b6f4ce1df..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_rendersetting.php +++ /dev/null @@ -1,60 +0,0 @@ - - * @author Sven Baumann - * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id'][0] = 'Attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id'][1] = 'Attribute this setting relates to.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template'][0] = 'Custom template to use for generating'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template'][1] = - 'Select the template that shall be used for the selected attribute. ' . - 'Valid template files start with "mm_<type>" where the type name is put for <type>'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['additional_class'][0] = 'Custom CSS class'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['additional_class'][1] = - 'Enter any CSS classes that you want get added to the output of this attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['title_legend'] = 'Type'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['advanced_legend'] = 'Advanced'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new'][0] = 'New'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new'][1] = 'Create new setting'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit'][0] = 'Edit setting'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit'][1] = 'Edit render setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['cut'][0] = 'Cut render setting definition'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['cut'][1] = 'Cut render setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['copy'][0] = 'Copy render setting definition'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['copy'][1] = 'Copy render setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['delete'][0] = 'Delete render setting'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['delete'][1] = 'Delete render setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['show'][0] = 'Render setting details'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['show'][1] = 'Show details of render setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['toggle'][0] = 'Toggle'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['toggle'][1] = - 'Toggle the state of render setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall'][0] = 'Add all'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall'][1] = 'Add all attributes to render setting'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['pastenew'][0] = 'Add new at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['pastenew'][1] = 'Add new after render setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_filtersetting']['row'] = '%s %s [%s]'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addAll_willadd'] = - 'Will add attribute "%s" [%s] to rendersetting.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addAll_alreadycontained'] = - 'Attribute "%s" [%s] already in rendersetting.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addAll_addsuccess'] = - 'Added attribute "%s" [%s] to rendersetting.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addAll_activate'] = 'Add new settings enabled.'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_rendersettings.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_rendersettings.php deleted file mode 100644 index 574b75dad..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_rendersettings.php +++ /dev/null @@ -1,83 +0,0 @@ - - * @author Ingolf Steinhardt - * @author Sven Baumann - * @copyright 2012-2020 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['title_legend'] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['general_legend'] = 'General settings'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['expert_legend'] = 'Expert settings'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpto_legend'] = 'Frontend jump-to settings'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['additional_legend'] = 'Additional files'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['name'][0] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['name'][1] = 'Setting name.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['template'][0] = 'Template'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['template'][1] = - 'The template to use to render the items.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['format'][0] = 'Output format'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['format'][1] = - 'Define the output format. Leave empty to use the format used by current page.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['formatOptions']['html5'] = 'HTML5'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['formatOptions']['text'] = 'Text'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo'][0] = 'JumpTo page'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo'][1] = - 'The page that shall be used as "show details" urls. Note that the detailed URL params will get generated by the ' . - 'filter setting that is currently in use.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo_allLanguages'] = 'All languages'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo_language'][0] = 'Language'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo_language'][1] = 'The language for the jump to page.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo_page'][0] = 'Jump to page'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo_page'][1] = 'The page to use for detail links.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo_filter'][0] = 'Filter settings'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['jumpTo_filter'][1] = - 'The filter settings that define how the target (the reader/lister on the detail page) will filter the items.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['hideEmptyValues'][0] = 'Hide empty values'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['hideEmptyValues'][1] = - 'Hide empty values in backend and frontend.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['hideLabels'][0] = 'Hide labels'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['hideLabels'][1] = - 'Hide all labels in backend and frontend.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['additionalCss'][0] = 'Additional css files'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['additionalCss'][1] = - 'Choose this, if you want to include additional css files.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['additionalJs'][0] = 'Additional javascript files'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['additionalJs'][1] = - 'Choose this, if you want to include additional javascript files.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['file'] = 'File'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['publish'] = 'Publish'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['new'][0] = 'New'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['new'][1] = 'Create new setting'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['edit'][0] = 'Edit setting'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['edit'][1] = 'Edit setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['copy'][0] = 'Copy setting definition'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['copy'][1] = 'Copy setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['delete'][0] = 'Delete setting'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['delete'][1] = 'Delete setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['show'][0] = 'Filter details'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['show'][1] = 'Show details of setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['settings'][0] = 'Define attribute settings'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['settings'][1] = - 'Define attribute settings for setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['pastenew'][0] = 'Add new at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['pastenew'][1] = 'Add new after setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['pasteafter'][0] = 'Create new setting at the top'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['pasteafter'][1] = 'Create new after setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['error_unknown_id'] = 'unknown ID: %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['error_unknown_attribute'] = 'unknown attribute'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersettings']['error_unknown_column'] = 'unknown column'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_searchable_pages.php b/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_searchable_pages.php deleted file mode 100644 index 2822003b2..000000000 --- a/src/CoreBundle/Resources/contao/languages/en/tl_metamodel_searchable_pages.php +++ /dev/null @@ -1,55 +0,0 @@ - - * @author Tim Becker - * @author Ingolf Steinhardt - * @author Sven Baumann - * @copyright 2012-2018 The MetaModels team. - * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later - * @filesource - */ - -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['name'][0] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['name'][1] = 'Name of the searchable page setting'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['tstamp'][0] = 'Revision date'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['tstamp'][1] = 'Date and time of the latest revision.'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['rendersetting'][0] = 'Rendersetting'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['rendersetting'][1] = - 'Choose the rendersetting which will be used for the search rendering.'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['filter'][0] = 'Filtersetting'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['filter'][1] = - 'Choose the filtersetting which will be used for the search rendering.'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['filterparams'][0] = 'Filter parameter override'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['filterparams'][1] = 'Filter parameter override'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['title_legend'] = 'Name'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['general_legend'] = 'General settings'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['new'][0] = 'New searchable page'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['new'][1] = 'Create new searchable page setting'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['edit'][0] = 'Edit searchable page'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['edit'][1] = 'Edit the searchable page setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['copy'][0] = 'Copy searchable page'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['copy'][1] = - 'Copy definition of searchable page setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['delete'][0] = 'Delete searchable page'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['delete'][1] = 'Delete searchable page setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['show'][0] = 'Searchable page setting details'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['show'][1] = - 'Show details of searchable page setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['toggle'][0] = 'Toggle'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['toggle'][1] = - 'Toggle the state of searchable page setting ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['settings'][0] = 'Searchable page settings'; -$GLOBALS['TL_LANG']['tl_metamodel_searchable_pages']['settings'][1] = - 'Edit the settings of searchable page setting ID %s'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_module.php b/src/CoreBundle/Resources/contao/languages/en/tl_module.php old mode 100644 new mode 100755 index d956b70d5..0caf3b184 --- a/src/CoreBundle/Resources/contao/languages/en/tl_module.php +++ b/src/CoreBundle/Resources/contao/languages/en/tl_module.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -41,7 +41,7 @@ 'Please specify the offset value (i.e. 10 to skip the first 10 items).'; $GLOBALS['TL_LANG']['tl_module']['metamodel_limit'][0] = 'Maximum number of items'; $GLOBALS['TL_LANG']['tl_module']['metamodel_limit'][1] = - 'Please enter the maximum number of items. Enter 0 to show all items and therefore disable the pagination.'; + 'Please enter the maximum number of items - enter 0 to show all items.'; $GLOBALS['TL_LANG']['tl_module']['metamodel_sortby'][0] = 'Order by'; $GLOBALS['TL_LANG']['tl_module']['metamodel_sortby'][1] = 'Please choose the sort order.'; $GLOBALS['TL_LANG']['tl_module']['metamodel_sortby_direction'][0] = 'Order by direction'; @@ -61,6 +61,9 @@ $GLOBALS['TL_LANG']['tl_module']['metamodel_order_dir_param'][0] = 'Override key for order direction'; $GLOBALS['TL_LANG']['tl_module']['metamodel_order_dir_param'][1] = 'You can override the default key \'orderDir\'.'; +$GLOBALS['TL_LANG']['tl_module']['metamodel_sort_urlfragment'][0] = 'URL fragment'; +$GLOBALS['TL_LANG']['tl_module']['metamodel_sort_urlfragment'][1] = + 'Add URL fragment to jump to anchor or id.'; $GLOBALS['TL_LANG']['tl_module']['metamodel_filtering'][0] = 'Filter settings to apply'; $GLOBALS['TL_LANG']['tl_module']['metamodel_filtering'][1] = 'Select the filter settings that shall get applied when compiling the list.'; @@ -81,9 +84,9 @@ $GLOBALS['TL_LANG']['tl_module']['metamodel_page_param_type'][0] = 'URL-Type for pagination'; $GLOBALS['TL_LANG']['tl_module']['metamodel_page_param_type'][1] = 'Please specify the type of URL parameters als slug (key\value) or GET (key=value).'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_maxpaginationlinks'][0] = +$GLOBALS['TL_LANG']['tl_module']['metamodel_maxpaginationlinks'][0] = 'Maximum number of pagination links'; -$GLOBALS['TL_LANG']['tl_module']['metamodel_maxpaginationlinks'][1] = +$GLOBALS['TL_LANG']['tl_module']['metamodel_maxpaginationlinks'][1] = 'Please enter the maximum number pagination links. Enter 0 to show the default value from Contao e.g. 7.'; $GLOBALS['TL_LANG']['tl_module']['metamodel_pagination'][0] = 'Custom template to use for pagination'; @@ -105,6 +108,10 @@ $GLOBALS['TL_LANG']['tl_module']['metamodel_jumpTo'][0] = 'Redirect page'; $GLOBALS['TL_LANG']['tl_module']['metamodel_jumpTo'][1] = 'Please choose the page to which visitors will be redirected when clicking a link or submitting a form.'; +$GLOBALS['TL_LANG']['tl_module']['metamodel_fef_id'][0] = 'Form ID'; +$GLOBALS['TL_LANG']['tl_module']['metamodel_fef_id'][1] = + 'Set ID as own postfix for "FORM_SUBMIT" value e.g. to use redirect and another filter -' . + ' in which case the value must be the same. '; $GLOBALS['TL_LANG']['tl_module']['metamodel_fef_urlfragment'][0] = 'URL fragment'; $GLOBALS['TL_LANG']['tl_module']['metamodel_fef_urlfragment'][1] = 'Add URL fragment to jump to anchor or id.'; @@ -142,9 +149,3 @@ $GLOBALS['TL_LANG']['tl_module']['metamodel_meta_description'][0] = 'Meta Description'; $GLOBALS['TL_LANG']['tl_module']['metamodel_meta_description'][1] = 'Set this attribute as the meta-description of the page.'; -$GLOBALS['TL_LANG']['tl_module']['editmetamodel'][0] = 'Edit MetaModel'; -$GLOBALS['TL_LANG']['tl_module']['editmetamodel'][1] = 'Edit the MetaModel ID %s.'; -$GLOBALS['TL_LANG']['tl_module']['editrendersetting'][0] = 'Edit render setting'; -$GLOBALS['TL_LANG']['tl_module']['editrendersetting'][1] = 'Edit the render setting ID %s.'; -$GLOBALS['TL_LANG']['tl_module']['editfiltersetting'][0] = 'Edit filter setting'; -$GLOBALS['TL_LANG']['tl_module']['editfiltersetting'][1] = 'Edit the filter setting ID %s.'; diff --git a/src/CoreBundle/Resources/contao/languages/en/tl_syncCto_database.php b/src/CoreBundle/Resources/contao/languages/en/tl_syncCto_database.php old mode 100644 new mode 100755 diff --git a/src/CoreBundle/Resources/contao/languages/et/default.php b/src/CoreBundle/Resources/contao/languages/et/default.php deleted file mode 100644 index e0669a075..000000000 --- a/src/CoreBundle/Resources/contao/languages/et/default.php +++ /dev/null @@ -1,62 +0,0 @@ -lihtne päring'; -$GLOBALS['TL_LANG']['XPL']['customsql']['3']['0'] = 'Sisesta silte'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['0']['0'] = 'Omaduse väärtus on...'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['2']['0'] = 'Kas omadus on nähtav...'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['3']['0'] = 'VÕI'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['4']['0'] = 'JA'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['5']['0'] = 'EI'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['0'] = 'clr'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['0'] = 'clx'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['0'] = 'w50'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['0'] = 'w50x'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['0'] = 'm12'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['0'] = 'juhendaja'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['8']['0'] = 'Pikk'; - diff --git a/src/CoreBundle/Resources/contao/languages/et/modules.php b/src/CoreBundle/Resources/contao/languages/et/modules.php deleted file mode 100644 index 8bfe53a98..000000000 --- a/src/CoreBundle/Resources/contao/languages/et/modules.php +++ /dev/null @@ -1,19 +0,0 @@ -requête plaine'; -$GLOBALS['TL_LANG']['XPL']['customsql']['2']['0'] = 'Example 2
insert tablename'; -$GLOBALS['TL_LANG']['XPL']['customsql']['3']['0'] = 'Balises d\'insertion'; -$GLOBALS['TL_LANG']['XPL']['customsql']['4']['0'] = 'Sécuriser les balises'; -$GLOBALS['TL_LANG']['XPL']['customsql']['5']['0'] = 'Source des paramètres
'; -$GLOBALS['TL_LANG']['XPL']['customsql']['6']['0'] = 'Example 3
Utilise des sources de paramètres de filtre complexe'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['3']['0'] = 'OR'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['0'] = 'clr'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['1'] = 'Effacer tous les flottants'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['0'] = 'clx'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['1'] = 'Enlever seulement le débordement caché ennuyeux, à utiliser avec "clr".'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['0'] = 'w50'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['1'] = 'Définir la largeur du champ à 50% et son type de flottant (float:left).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['0'] = 'w50x'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['1'] = 'Retirer seulement la hauteur fixe ennuyeuse, à utiliser avec "w50".'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['0'] = 'm12'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['1'] = 'Ajoute 12 pixels à la marge haute de l\'élément (utilisé pour les cases à cocher simples).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['0'] = 'wizard'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['1'] = 'Raccourcir le champ de saisie de sorte qu\'il y ait assez de place pour le bouton Assistant (ex : champ sélecteur de date).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['8']['0'] = 'long'; - diff --git a/src/CoreBundle/Resources/contao/languages/fr/modules.php b/src/CoreBundle/Resources/contao/languages/fr/modules.php deleted file mode 100644 index 8a2bb18e0..000000000 --- a/src/CoreBundle/Resources/contao/languages/fr/modules.php +++ /dev/null @@ -1,25 +0,0 @@ -"auto_item" peut aussi être utilisé, spécialement pour les alias de colonnes.'; - diff --git a/src/CoreBundle/Resources/contao/languages/fr/tl_metamodel_item.php b/src/CoreBundle/Resources/contao/languages/fr/tl_metamodel_item.php deleted file mode 100644 index 854d39d51..000000000 --- a/src/CoreBundle/Resources/contao/languages/fr/tl_metamodel_item.php +++ /dev/null @@ -1,46 +0,0 @@ -%s [%s]'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['0'] = 'Ajouter tout'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['1'] = 'Ajouter tout les attributs au paramètre de rendu'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['advanced_legend'] = 'Avancé'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['0'] = 'Attribut'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['1'] = 'l\'attribut auquel ce paramètre se réfère.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit']['0'] = 'Éditer le paramètre'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['0'] = 'Nouveau'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['1'] = 'Créer nouveau paramètre'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['0'] = 'Modèle personnalisé à utiliser pour la production'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['1'] = 'Sélectionner le modèle qui doit être utilisé pour l\'attribut sélectionné. les fichiers modèle valides commencent par "mm_<type>" où le nom type est mis pour <type>'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['title_legend'] = 'Type'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['toggle']['0'] = 'Basculer'; - diff --git a/src/CoreBundle/Resources/contao/languages/fr/tl_metamodel_rendersettings.php b/src/CoreBundle/Resources/contao/languages/fr/tl_metamodel_rendersettings.php deleted file mode 100644 index 9a3529884..000000000 --- a/src/CoreBundle/Resources/contao/languages/fr/tl_metamodel_rendersettings.php +++ /dev/null @@ -1,65 +0,0 @@ -query semplice'; -$GLOBALS['TL_LANG']['XPL']['customsql']['2']['0'] = 'Esempio 2
Riferimento nome tabella'; -$GLOBALS['TL_LANG']['XPL']['customsql']['3']['0'] = 'Insert tags'; -$GLOBALS['TL_LANG']['XPL']['customsql']['4']['0'] = 'Secure insert tags'; -$GLOBALS['TL_LANG']['XPL']['customsql']['5']['0'] = 'Sorgente dei parametri'; -$GLOBALS['TL_LANG']['XPL']['customsql']['6']['0'] = 'Esempio 3
Utilizzo di filtri di sorgenti parametri complessi '; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['3']['0'] = 'OR'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['0'] = 'clr'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['1'] = 'Elimina tutti i floats.'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['0'] = 'clx'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['1'] = 'Rimuove solo l\'overflow hidden fastidioso. Si prega di utilizzare insieme con "clr".'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['0'] = 'w50'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['1'] = 'Imposta la larghezza del campo al 50% e con float (float:left)'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['0'] = 'w50x'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['1'] = 'Rimuove solo l\'altezza fissa fastidiosa. Si prega di utilizzare insieme con "w50".'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['0'] = 'm12'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['1'] = 'Aggiungi 12 pixel al top margin dell\'elemento (utilizzato per i checkbox singoli)'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['0'] = 'assistente'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['1'] = 'Accorciare il campo di input per permettere di avere un pulsante di aiuto sul campo (esempio nei campi data per far comparire il calendario)'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['8']['0'] = 'lungo'; - diff --git a/src/CoreBundle/Resources/contao/languages/it/modules.php b/src/CoreBundle/Resources/contao/languages/it/modules.php deleted file mode 100644 index cb9b5cea1..000000000 --- a/src/CoreBundle/Resources/contao/languages/it/modules.php +++ /dev/null @@ -1,25 +0,0 @@ -"auto_item" può essere utilizzato, questo è particolarmente utile come alias delle colonne.'; - diff --git a/src/CoreBundle/Resources/contao/languages/it/tl_metamodel_item.php b/src/CoreBundle/Resources/contao/languages/it/tl_metamodel_item.php deleted file mode 100644 index 76163960c..000000000 --- a/src/CoreBundle/Resources/contao/languages/it/tl_metamodel_item.php +++ /dev/null @@ -1,46 +0,0 @@ -%s [%s]'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['0'] = 'Aggiungi tutti'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['1'] = 'Aggiungi tutti gli attributi nelle impostazioni del render'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['advanced_legend'] = 'Avanzato'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['0'] = 'Attributo'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['1'] = 'Attributo a cui questa impostazione si riferisce.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit']['0'] = 'Modifica l\'impostazione'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['0'] = 'Nuovo'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['1'] = 'Crea una nuova impostazione'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['0'] = 'Template personalizzato da utilizzare per la generazione'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['1'] = 'Selezionare il template che deve essere utilizzato per l\'attributo selezionato. Il nome del template deve iniziare con "mm_<type>" dove il nome del tipo deve essere inserito al posto di <type>'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['title_legend'] = 'Tipo'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['toggle']['0'] = 'Toggle'; - diff --git a/src/CoreBundle/Resources/contao/languages/it/tl_metamodel_rendersettings.php b/src/CoreBundle/Resources/contao/languages/it/tl_metamodel_rendersettings.php deleted file mode 100644 index 5c9667f2c..000000000 --- a/src/CoreBundle/Resources/contao/languages/it/tl_metamodel_rendersettings.php +++ /dev/null @@ -1,65 +0,0 @@ -dumonda simpla'; -$GLOBALS['TL_LANG']['XPL']['customsql']['2']['0'] = 'Exempel 2
inserir num da tabella'; -$GLOBALS['TL_LANG']['XPL']['customsql']['3']['0'] = 'Insert-tags'; -$GLOBALS['TL_LANG']['XPL']['customsql']['4']['0'] = 'Insert-tags segirs'; -$GLOBALS['TL_LANG']['XPL']['customsql']['5']['0'] = 'Funtaunas da parameters
'; -$GLOBALS['TL_LANG']['XPL']['customsql']['6']['0'] = 'Exempel 3
utilisar funtaunas da parameters cumplexas'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['3']['0'] = 'OR'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['0'] = 'clr'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['1'] = 'Annular tut ils floats.'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['0'] = 'clx'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['1'] = 'Allontanar l\'overflow zuppentà mulestus. Utilisar ensemen cun "clr".'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['0'] = 'w50'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['1'] = 'Definir la ladezza dal champ a 50% e laschar circumdar (float:left).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['0'] = 'w50x'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['1'] = 'Allontanar la autezza fixa mulestusa. Utilisar ensemen cun "w50".'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['0'] = 'm12'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['1'] = 'Agiuntar sura a l\'element in ur da 12 pixels (vegn utilisà per chaschettas da controlla singulas).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['0'] = 'assistent'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['1'] = 'Scursanir il champ d\'endataziun per ch\'i haja avunda plaz per in buttun d\'assistenza (p.ex. champs da tscherner la data).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['8']['0'] = 'long'; - diff --git a/src/CoreBundle/Resources/contao/languages/rm/modules.php b/src/CoreBundle/Resources/contao/languages/rm/modules.php deleted file mode 100644 index 0a8eed86a..000000000 --- a/src/CoreBundle/Resources/contao/languages/rm/modules.php +++ /dev/null @@ -1,26 +0,0 @@ -"auto_item" po era vegnir utilisà, quai è specialmain practic per colonnas dad alias.'; - diff --git a/src/CoreBundle/Resources/contao/languages/rm/tl_metamodel_item.php b/src/CoreBundle/Resources/contao/languages/rm/tl_metamodel_item.php deleted file mode 100644 index d626876ee..000000000 --- a/src/CoreBundle/Resources/contao/languages/rm/tl_metamodel_item.php +++ /dev/null @@ -1,46 +0,0 @@ -%s [%s]'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['0'] = 'Agiuntar tuts'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['1'] = 'Agiuntar tut ils attributs a la configuraziun da visualisar'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['advanced_legend'] = 'Avanzà'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['0'] = 'Attribut'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['1'] = 'Attribut che vegn definì da questa configuraziun.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit']['0'] = 'Modifitgar la configuraziun'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['0'] = 'Nov'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['1'] = 'Crear ina nova configuraziun'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['0'] = 'Template persunalisà per generar'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['1'] = 'Tscherna il template che duai vegnir utilisà per l\'attribut tschernì. Datotecas da template validas cumenzan cun "mm_<type>", e <type> stat per il tip.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['title_legend'] = 'Tip'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['toggle']['0'] = 'Activar/deactivar'; - diff --git a/src/CoreBundle/Resources/contao/languages/rm/tl_metamodel_rendersettings.php b/src/CoreBundle/Resources/contao/languages/rm/tl_metamodel_rendersettings.php deleted file mode 100644 index 34911d36e..000000000 --- a/src/CoreBundle/Resources/contao/languages/rm/tl_metamodel_rendersettings.php +++ /dev/null @@ -1,65 +0,0 @@ - веб-сайте - в разделе "Помощь" в качестве справочника по MetaModels - вы можете найти руководство, все каналы поддержки, - видеоуроки и нашу рассылку новостей.

- - Прямая ссылка на руководство MetaModels...'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['main_headline'] = 'Мы просим оказать нам помощь!'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['main_text'] = 'Как разработчики этого проекта, мы не получаем никакой компенсации - за нашу работу. Значительная часть нашего ценного свободного - времени предоставляется проекту MetaModels. Любые пожертвования - на проект MetaModels позволят нам работать над проектом в рабочее - время, значительно ускоряя разработку. -

Мы гарантируем, что потратим любые пожертвования - только на этот проект. Если у вас есть какие-либо конкретные - требования или пожелания, вы можете нанять члена - нашей команды, который с радостью поможет вам.

  - Большое спасибо всем нашим существующим сторонникам и - спонсорам.. Пожалуйста, подумайте о присоединении к нашему - списку спонсоров. Мы не смогли бы создать такой популярный проект - с открытым исходным кодом без поддержки сообщества.

- Больше информации'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['other_donations'] = 'Для пожертвований проверьте также наш - - поиск финансирования или рассмотрите - - пожертвование для авторов.'; -$GLOBALS['TL_LANG']['MSC']['metamodels_support']['purpose'] = 'Цель'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_filter']['0'] = 'Фи'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_filter']['1'] = 'Фильтр'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_name']['0'] = 'MM'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_name']['1'] = 'MetaModel'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_render_setting']['0'] = 'Нв'; -$GLOBALS['TL_LANG']['MSC']['mm_be_info_render_setting']['1'] = 'Настройка визуализации'; -$GLOBALS['TL_LANG']['MSC']['noItemsMsg'] = 'Нет элементов, соответствующих вашему запросу.'; -$GLOBALS['TL_LANG']['MSC']['no_theme'] = 'global scope'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['filter']['0'] = 'Фильтр'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['limit']['0'] = 'Лимит'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['search']['0'] = 'Поиск'; -$GLOBALS['TL_LANG']['MSC']['panelLayout']['sort']['0'] = 'Сортировка'; -$GLOBALS['TL_LANG']['MSC']['random'] = 'Случайный'; -$GLOBALS['TL_LANG']['MSC']['template_in_theme'] = '%s (%s)'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clr']['0'] = 'clr'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clr']['1'] = 'Очистить все floats.'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clx']['0'] = 'clx'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['clx']['1'] = 'Чтобы удалить только раздражающее переполнение, используйте его вместе с «clr».'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['long']['0'] = 'длинный'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['long']['1'] = 'Сделать поле ввода текста двумя столбцами.'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['m12']['0'] = 'm12'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['m12']['1'] = 'Добавьте верхний край 12 пикселей к элементу (используется для отдельных флажков).'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50']['0'] = 'w50'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50']['1'] = 'Установите ширину поля на 50% и поместите ее (float: left).'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50x']['0'] = 'w50x'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['w50x']['1'] = 'Чтобы удалить только раздражающую фиксированную высоту, используйте ее вместе с «w50».'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['wizard']['0'] = 'мастер'; -$GLOBALS['TL_LANG']['MSC']['tl_class']['wizard']['1'] = 'Сократите поле ввода, чтобы было достаточно места для кнопки мастера (например, поля выбора даты).'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['action_add'] = '+'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['action_remove'] = '-'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['clear_all'] = 'Очистить все фильтры'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['do_not_filter'] = 'Без фильтрации'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['no_combinations'] = '(Комбинаций соответствия не найдено.)'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['select_all'] = 'Выбрать все'; -$GLOBALS['TL_LANG']['metamodels_frontendfilter']['submit'] = 'Фильтр'; - diff --git a/src/CoreBundle/Resources/contao/languages/ru/explain.php b/src/CoreBundle/Resources/contao/languages/ru/explain.php deleted file mode 100644 index b59647516..000000000 --- a/src/CoreBundle/Resources/contao/languages/ru/explain.php +++ /dev/null @@ -1,85 +0,0 @@ -простой запрос'; -$GLOBALS['TL_LANG']['XPL']['customsql']['2']['0'] = 'Пример 2
вставка имени таблицы'; -$GLOBALS['TL_LANG']['XPL']['customsql']['3']['0'] = 'Вставка тегов'; -$GLOBALS['TL_LANG']['XPL']['customsql']['3']['1'] = 'Вставка тегов поддерживается, но имейте в виду, что не все теги могут - быть доступны когда используется параметр фильтра - (например, {{page::id}} доступен только при использовании - с передней страницы, а не из RSS-каналов).'; -$GLOBALS['TL_LANG']['XPL']['customsql']['4']['0'] = 'Безопасная вставка тегов'; -$GLOBALS['TL_LANG']['XPL']['customsql']['4']['1'] = 'Безопасные теги вставки похожи на теги простой вставки, но их значение ускользает в запросе.
- Поэтому, возможно, вам лучше использовать безопасный эквивалент, если вы точно не знаете, что делаете.
- Обозначения: -
{{secure::page::id}}
'; -$GLOBALS['TL_LANG']['XPL']['customsql']['5']['0'] = 'Параметр источников'; -$GLOBALS['TL_LANG']['XPL']['customsql']['5']['1'] = 'Источники параметров имеют нормальную компоновку: -
{{param::[source]?[query string]}}
- Где источником может быть любой из: -
    -
  • get - строка запроса HTTP GET
  • -
  • post - HTTP POST поля
  • -
  • cookie - HTTP COOKIE значения
  • -
  • session - любое поле в the Contao сессии
  • -
  • filter - любой из переданных параметров фильтра (для обмена параметрами между настройками фильтра).
  • -
  • container - имя вызываемой службы в контейнере службы MetaModels (для этого требуется дополнительное PHP-кодирование с вашей стороны).
  • -
- Строка запроса построена как обычная строка запроса HTTP как пары «имя = значение», которые объединены с помощью символа & char и должны содержать по крайней мере поле «имя». - Можно использовать одну или несколько следующих дополнительных клавиш: -
    -
  • default - значение по умолчанию для использования, если нет доступного значения.
  • -
  • aggregate - либо «список», либо «набор».
  • -
  • key - установить 1, чтобы прочитать ключ массивов (требуется совокупность с «set»).
  • -
  • recursive - устанавливается в 1 для чтения рекурсивных массивов (требуется совокупность с «set»).
  • -
  • service - имя службы для извлечения (требуется источник «service»).
  • -
- '; -$GLOBALS['TL_LANG']['XPL']['customsql']['6']['0'] = 'Пример 3
использовать сложный фильтр параметров источников'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['0']['0'] = 'Значение атрибута...'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['0']['1'] = 'Условие выполняется, когда значение атрибута равно заданному значению.'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['1']['0'] = 'Значения атрибута содержат любое из...'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['1']['1'] = 'Условие выполняется, когда любое из значений атрибута соответствует хотя бы одному из указанных значений (установить пересечение).'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['2']['0'] = 'Является ли атрибут видимым...'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['2']['1'] = 'Условие выполняется, когда выполняется условие указанного атрибута. Другими словами, атрибут отображается, если и только если указанный атрибут также виден.'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['3']['0'] = 'ИЛИ'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['3']['1'] = 'Любое дополнительное условие должно быть выполнено.'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['4']['0'] = 'И'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['4']['1'] = 'Все вспомогательные условия должны быть выполнены.'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['5']['0'] = 'НЕ'; -$GLOBALS['TL_LANG']['XPL']['dcasetting_condition']['5']['1'] = 'Инвертировать результат содержащегося условия.'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['0'] = 'clr'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['2']['1'] = 'Очистить все floats.'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['0'] = 'clx'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['3']['1'] = 'Чтобы удалить только раздражающее переполнение, используйте его вместе с «clr».'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['0'] = 'w50'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['4']['1'] = 'Установите ширину поля на 50% и поместите ее (float: left).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['0'] = 'w50x'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['5']['1'] = 'Чтобы удалить только раздражающую фиксированную высоту, используйте ее вместе с «w50».'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['0'] = 'm12'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['6']['1'] = 'Добавьте верхний край 12 пикселей к элементу (используется для отдельных флажков).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['0'] = 'мастер'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['7']['1'] = 'Сократите поле ввода, чтобы было достаточно места для кнопки мастера (например, поля выбора даты).'; -$GLOBALS['TL_LANG']['XPL']['tl_class']['8']['0'] = 'длинный'; - diff --git a/src/CoreBundle/Resources/contao/languages/ru/modules.php b/src/CoreBundle/Resources/contao/languages/ru/modules.php deleted file mode 100644 index 9035f46aa..000000000 --- a/src/CoreBundle/Resources/contao/languages/ru/modules.php +++ /dev/null @@ -1,26 +0,0 @@ -ПРИМЕЧАНИЕ: Это будет неявно активным, если вы выбрали «Уникальные значения» в конфигурации атрибута.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['mandatory_for_unique_attr'] = 'Уникальные атрибуты автоматически обязательны (это невозможно изменить).'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['name_langcode'] = 'Язык'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['name_value'] = 'Название легенды'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['new']['0'] = 'Новый'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['new']['1'] = 'Создать новую настройку'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pasteafter']['0'] = 'Создать новую настройку сверху'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pasteafter']['1'] = 'Создать новый после настройки ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pastenew']['0'] = 'Добавить новый сверху'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['pastenew']['1'] = 'Добавить новый после настройки ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['presentation_legend'] = 'Связанные параметры внешнего вида виджета'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['preserveTags']['0'] = 'Не кодировать все теги html.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['preserveTags']['1'] = 'Если вы выберете это, HTML-теги не будут закодированы.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['readonly']['0'] = 'Только чтение'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['readonly']['1'] = 'Если истина, виджет будет доступен только для чтения и не может быть изменен.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['readonly_for_force_alias'] = 'Атрибуты с принудительным псевдонимом автоматически считываются (это невозможно изменить).'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rows']['0'] = 'Строки'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rows']['1'] = 'Количество строк для виджета longtext/table.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rte']['0'] = 'Включить редактор расширенного текста'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['rte']['1'] = 'Выберите конфигурацию расширенного текста, которая будет использоваться в этом поле (если есть).'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['searchable']['0'] = 'Поисковый'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['searchable']['1'] = 'Выберите, если этот атрибут должен быть доступен для поиска в панели управления.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['show']['0'] = 'Детали настроек'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['show']['1'] = 'Показать детали настроек ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['spaceToUnderscore']['0'] = 'Заменять пробелы на подчеркивания'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['spaceToUnderscore']['1'] = 'Если истина, любой пробел будет заменен подчеркиванием.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['submitOnChange']['0'] = 'Отправить по изменению'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['submitOnChange']['1'] = 'Если активно, форма будет отправлена ​​при изменении значения поля.'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['template']['0'] = 'Пользовательские шаблоны для генерации'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['template']['1'] = 'Выберите шаблон, который должен использоваться для выбранного атрибута. Названия шаблонов начинаются с "mm_<type>", где название типа ставится для <type>'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['title_legend'] = 'Тип'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['tl_class']['0'] = 'Класс панели управления'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['toggle']['0'] = 'Переключить'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['toggle']['1'] = 'Переключить состояние настройки ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash']['0'] = 'Обработка замыкающего слеша'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash']['1'] = 'Здесь вы можете указать, как обрабатывать конечные слеши'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash_options']['0'] = 'Сбросить косую черту при сохранении'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash_options']['1'] = 'Добавить косую черту при сохранении'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting']['trailingSlash_options']['2'] = 'Ничего не делать'; - diff --git a/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_dcasetting_condition.php b/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_dcasetting_condition.php deleted file mode 100644 index de055910b..000000000 --- a/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_dcasetting_condition.php +++ /dev/null @@ -1,59 +0,0 @@ -%s
для атрибута %s (Параметр: %s)'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['typedesc']['conditionand'] = '%s %s
все вспомогательные условия должны быть выполнены'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['typedesc']['conditionnot'] = '%s %s
инвертировать результат содержащегося условия'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['typedesc']['conditionor'] = '%s %sлюбые условия должны быть выполнены'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['value']['0'] = 'Значение'; -$GLOBALS['TL_LANG']['tl_metamodel_dcasetting_condition']['value']['1'] = 'Выберите нужное значение.'; - diff --git a/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_filter.php b/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_filter.php deleted file mode 100644 index 8a107a3bb..000000000 --- a/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_filter.php +++ /dev/null @@ -1,35 +0,0 @@ -"auto_item", что особенно полезно для столбцов псевдонимов.'; - diff --git a/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_item.php b/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_item.php deleted file mode 100644 index 769878dd6..000000000 --- a/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_item.php +++ /dev/null @@ -1,52 +0,0 @@ -%s [%s]'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addAll_activate'] = 'Добавить новые настройки.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['0'] = 'Добавить все'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['addall']['1'] = 'Добавить все атрибуты к настройкам визуализации'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['additional_class']['0'] = 'Пользовательский CSS-класс'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['additional_class']['1'] = 'Введите все классы CSS, которые вы хотите добавить в вывод этого атрибута'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['advanced_legend'] = 'Дополнительно'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['0'] = 'Атрибут'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['attr_id']['1'] = 'Атрибут этой настройки относится к.'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['copy']['0'] = 'Копировать настройки визуализации определения'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['copy']['1'] = 'Копировать настройки визуализации ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['cut']['0'] = 'Вырезать настройки визуализации определения'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['cut']['1'] = 'Вырезать настройки визуализации ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['delete']['0'] = 'Удалить настройки визуализации'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['delete']['1'] = 'Удалить настройки визуализации ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit']['0'] = 'Редактировать настройки'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['edit']['1'] = 'Изменить настройки визуализации ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['0'] = 'Новый'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['new']['1'] = 'Создать новую настройку'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['pastenew']['0'] = 'Добавить новый сверху'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['pastenew']['1'] = 'Добавить новый после настройки визуализации ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['show']['0'] = 'Настройки визуализации деталей'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['show']['1'] = 'Показать детали визуализации настроек ID %s'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['0'] = 'Пользовательские шаблоны для генерации'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['template']['1'] = 'Выберите шаблон, который должен использоваться для выбранного атрибута. Имена шаблонов начинаются с "mm_<type>" где имя типа ставится для <type>'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['title_legend'] = 'Тип'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['toggle']['0'] = 'Переключить'; -$GLOBALS['TL_LANG']['tl_metamodel_rendersetting']['toggle']['1'] = 'Переключить состояние настройки визуализации ID %s'; - diff --git a/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_rendersettings.php b/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_rendersettings.php deleted file mode 100644 index 4073e95c9..000000000 --- a/src/CoreBundle/Resources/contao/languages/ru/tl_metamodel_rendersettings.php +++ /dev/null @@ -1,73 +0,0 @@ - - backBt ?>
diff --git a/src/CoreBundle/Resources/contao/templates/be_detectedproblems.html5 b/src/CoreBundle/Resources/contao/templates/be_detectedproblems.html5 index ac55ab6cc..8bba835ea 100644 --- a/src/CoreBundle/Resources/contao/templates/be_detectedproblems.html5 +++ b/src/CoreBundle/Resources/contao/templates/be_detectedproblems.html5 @@ -1,5 +1,5 @@ diff --git a/src/CoreBundle/Resources/contao/templates/metamodel_prerendered.html5 b/src/CoreBundle/Resources/contao/templates/metamodel_prerendered.html5 index a5e96c815..d89969706 100644 --- a/src/CoreBundle/Resources/contao/templates/metamodel_prerendered.html5 +++ b/src/CoreBundle/Resources/contao/templates/metamodel_prerendered.html5 @@ -1,3 +1,6 @@ +get('translator'); +?> data)): ?>
@@ -6,18 +9,14 @@ block('item'); ?>
- $strName): ?> - getFormat()][$field]) - && ($strValue = $arrItem[$this->getFormat()][$field]) - || (isset($arrItem['text'][$field]) && ($strValue = $arrItem['text'][$field])))): ?> + $name): ?> + getFormat()][$field] ?? null)) + || (null !== ($value = $arrItem['text'][$field] ?? null))): ?>
view->get('hideLabels')): ?> -
+
trans('field_label', ['%field_label%' => $name], 'metamodels_list') ?>
-
+
diff --git a/src/CoreBundle/Resources/contao/templates/metamodel_prerendered.text b/src/CoreBundle/Resources/contao/templates/metamodel_prerendered.text index ad78c65c3..c12497fbd 100644 --- a/src/CoreBundle/Resources/contao/templates/metamodel_prerendered.text +++ b/src/CoreBundle/Resources/contao/templates/metamodel_prerendered.text @@ -1,13 +1,14 @@ get('translator'); $hideLabels = $this->view->get('hideLabels'); if (count($this->data)) { foreach ($this->data as $item) { foreach ($item['attributes'] as $field => $name) { - if ((isset($item['text'][$field]) && ($value = $item['text'][$field]))) { + if (null !== ($value = $arrItem['text'][$field] ?? null)) { if (!$hideLabels) { - echo sprintf($GLOBALS['TL_LANG']['MSC']['field_label'], $name) . ' '; + echo trim($translator->trans('field_label', ['%field_label%' => $name], 'metamodels_list')) . ' '; } echo $value . PHP_EOL; } diff --git a/src/CoreBundle/Resources/contao/templates/metamodel_unrendered.html5 b/src/CoreBundle/Resources/contao/templates/metamodel_unrendered.html5 index df6ecc00d..ea8d295b0 100644 --- a/src/CoreBundle/Resources/contao/templates/metamodel_unrendered.html5 +++ b/src/CoreBundle/Resources/contao/templates/metamodel_unrendered.html5 @@ -1,20 +1,20 @@ +get('translator'); +?> items->getCount()): ?>
items->parseAll($this->getFormat(), $this->view) as $arrItem): ?> block('item'); ?>
- items->getItem()->getMetaModel()->getAttributes() as $field => $objAttribute): ?> - getFormat()][$field]) - || ($strValue = $arrItem['text'][$field])): ?> + items->getItem()->getMetaModel()->getAttributes() as $field => $attribute): ?> + getFormat()][$field] ?? null)) + || (null !== ($value = $arrItem['text'][$field] ?? null))): ?>
view->get('hideLabels')): ?> -
getName() - ) ?>
+
trans('field_label', ['%field_label%' => $attribute->getName()], 'metamodels_list') ?>
-
+
diff --git a/src/CoreBundle/Resources/contao/templates/metamodel_unrendered.text b/src/CoreBundle/Resources/contao/templates/metamodel_unrendered.text index 4607e29ea..ff3fd4997 100644 --- a/src/CoreBundle/Resources/contao/templates/metamodel_unrendered.text +++ b/src/CoreBundle/Resources/contao/templates/metamodel_unrendered.text @@ -1,4 +1,5 @@ get('translator'); $hideLabels = $this->view->get('hideLabels'); @@ -7,7 +8,7 @@ if ($this->items->getCount()) { foreach ($item['attributes'] as $field => $attribute) { if ((isset($item['text'][$field]) && ($value = $item['text'][$field]))) { if (!$hideLabels) { - echo sprintf($GLOBALS['TL_LANG']['MSC']['field_label'], $attribute->getName()) . ' '; + echo trim($translator->trans('field_label', ['%field_label%' => $attribute->getName()], 'metamodels_list')) . ' '; } echo $value . PHP_EOL; } diff --git a/src/CoreBundle/Resources/contao/templates/mm_actionbutton.html5 b/src/CoreBundle/Resources/contao/templates/mm_actionbutton.html5 index 512d14986..7ea5f73c8 100644 --- a/src/CoreBundle/Resources/contao/templates/mm_actionbutton.html5 +++ b/src/CoreBundle/Resources/contao/templates/mm_actionbutton.html5 @@ -1,5 +1,5 @@ action['html'])): ?> - action['class']): ?> class="action['class']; ?>"action['title']): ?> title="action['title']; ?>"action['attribute']; ?>>action['label']; ?> + action['class'] ?? null): ?> class="action['class'] ?? '' ?>"action['title'] ?? null): ?> title="action['title'] ?>"action['attribute'] ?? '' ?>>action['label'] ?> action['html'] ?> diff --git a/src/CoreBundle/Resources/contao/templates/mm_clearall_default.html5 b/src/CoreBundle/Resources/contao/templates/mm_clearall_default.html5 index e54c3183f..b8572b63f 100644 --- a/src/CoreBundle/Resources/contao/templates/mm_clearall_default.html5 +++ b/src/CoreBundle/Resources/contao/templates/mm_clearall_default.html5 @@ -1,6 +1,6 @@
cssID ?>style): ?> style="style ?>"> - +
diff --git a/src/CoreBundle/Resources/contao/templates/mm_filter_default.html5 b/src/CoreBundle/Resources/contao/templates/mm_filter_default.html5 index 46cc1452e..ee4b43f42 100644 --- a/src/CoreBundle/Resources/contao/templates/mm_filter_default.html5 +++ b/src/CoreBundle/Resources/contao/templates/mm_filter_default.html5 @@ -10,7 +10,7 @@
filters) ? $this->filters : []) as $filter): ?> -
> +
>
diff --git a/src/CoreBundle/Resources/contao/templates/mm_filteritem_linklist.html5 b/src/CoreBundle/Resources/contao/templates/mm_filteritem_linklist.html5 index 0b32246ec..8a60235ac 100644 --- a/src/CoreBundle/Resources/contao/templates/mm_filteritem_linklist.html5 +++ b/src/CoreBundle/Resources/contao/templates/mm_filteritem_linklist.html5 @@ -19,6 +19,7 @@ ?>
  • diff --git a/src/CoreBundle/Resources/contao/templates/mm_pagination.html5 b/src/CoreBundle/Resources/contao/templates/mm_pagination.html5 index 373001b81..1c55caedf 100644 --- a/src/CoreBundle/Resources/contao/templates/mm_pagination.html5 +++ b/src/CoreBundle/Resources/contao/templates/mm_pagination.html5 @@ -1,5 +1,5 @@ get('translator'); +$translator = \Contao\System::getContainer()->get('translator'); $paginationFragment = $this->paginationFragment ? '#' . $this->paginationFragment : ''; ?> diff --git a/src/CoreBundle/Resources/contao/templates/mod_metamodel_list.html5 b/src/CoreBundle/Resources/contao/templates/mod_metamodel_list.html5 index 0482b9a0a..5e34a57b3 100644 --- a/src/CoreBundle/Resources/contao/templates/mod_metamodel_list.html5 +++ b/src/CoreBundle/Resources/contao/templates/mod_metamodel_list.html5 @@ -1,8 +1,5 @@ extend($this->searchable ? 'block_searchable' : 'block_unsearchable'); ?> block('content'); ?> -editEnable): ?> - - items ?> pagination ?> endblock(); ?> diff --git a/src/CoreBundle/Resources/public/css/style.css b/src/CoreBundle/Resources/public/css/style.css index 9b615cb3b..34c266dd2 100644 --- a/src/CoreBundle/Resources/public/css/style.css +++ b/src/CoreBundle/Resources/public/css/style.css @@ -1,7 +1,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Carolina Koehn - * @copyright 2012-2022 The MetaModels team. + * @author Cliff Parnitzky + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -43,7 +44,7 @@ color: #8ab858; margin: 6px 0; padding-left: 24px; - background: url("../../../../themes/flexible/images/palOpen.gif") 3px center no-repeat; + background: url("/system/themes/flexible/icons/navcol.svg") 3px center no-repeat; } .mm_problem_display { @@ -153,7 +154,8 @@ form[id^=mm_] .sort_hint { } .widget.translat-attr > h3 > label, -.widget.translat-attr > fieldset > legend { +.widget.translat-attr > fieldset > legend, +.widget.translat-attr > .tl_checkbox_single_container label { padding-left: 20px; display: inline-block; background: url("../images/icons/locale.png") no-repeat left center; diff --git a/src/CoreBundle/Resources/public/scss/style.scss b/src/CoreBundle/Resources/public/scss/style.scss index b6280f785..8eb1c77c3 100644 --- a/src/CoreBundle/Resources/public/scss/style.scss +++ b/src/CoreBundle/Resources/public/scss/style.scss @@ -23,7 +23,7 @@ $icon_fields: url('../images/icons/fields.png'); $icon_dca_add: url('../images/icons/dca_add.png'); $icon_rendersettings_add: url('../images/icons/rendersettings_add.png'); -$icon_pal_open: url('../../../../themes/default/images/palOpen.gif'); +$icon_pal_open: url('/system/themes/flexible/icons/navcol.svg'); $icon_filter_setting: url('../images/icons/filter_settings.png'); $icon_locale: url('../images/icons/locale.png'); diff --git a/src/CoreBundle/Resources/translations/metamodels_default.en.xlf b/src/CoreBundle/Resources/translations/metamodels_default.en.xlf new file mode 100644 index 000000000..3db199390 --- /dev/null +++ b/src/CoreBundle/Resources/translations/metamodels_default.en.xlf @@ -0,0 +1,16 @@ + + + + + + global scope + + + default + + + %template% (%themes%) + + + + diff --git a/src/CoreBundle/Resources/translations/metamodels_filter.en.xlf b/src/CoreBundle/Resources/translations/metamodels_filter.en.xlf new file mode 100644 index 000000000..a4c91a2b7 --- /dev/null +++ b/src/CoreBundle/Resources/translations/metamodels_filter.en.xlf @@ -0,0 +1,13 @@ + + + + + + Filter value for attribute "%id%" + + + + + + + diff --git a/src/CoreBundle/Resources/translations/metamodels_list.en.xlf b/src/CoreBundle/Resources/translations/metamodels_list.en.xlf new file mode 100644 index 000000000..10c98b6a7 --- /dev/null +++ b/src/CoreBundle/Resources/translations/metamodels_list.en.xlf @@ -0,0 +1,25 @@ + + + + + + Sorting + + + Random + + + Id + + + %field_label%: + + + There are no items matching your search. + + + Details + + + + diff --git a/src/CoreBundle/Resources/translations/metamodels_navigation.en.xlf b/src/CoreBundle/Resources/translations/metamodels_navigation.en.xlf new file mode 100644 index 000000000..b588d11f3 --- /dev/null +++ b/src/CoreBundle/Resources/translations/metamodels_navigation.en.xlf @@ -0,0 +1,53 @@ + + + + + + MetaModels + + + MetaModels + + + The MetaModels extension allows you to create own data models. + + + MetaModels + + + Attributes of "%s" + + + All render setting of "%s" + + + Render settings in "%s" + + + Sorting and grouping in "%s" + + + All input screens of "%s" + + + Input screens in "%s" + + + Input screen and render setting combination for "%s" + + + Visibility conditions for attribute "%s" + + + All filter of "%s" + + + Filter settings in "%s" + + + All indexes of "%s" + + + + diff --git a/src/CoreBundle/Resources/translations/metamodels_support.en.xlf b/src/CoreBundle/Resources/translations/metamodels_support.en.xlf new file mode 100644 index 000000000..82d1902dc --- /dev/null +++ b/src/CoreBundle/Resources/translations/metamodels_support.en.xlf @@ -0,0 +1,62 @@ + + + + + + Support MetaModels + + + Support information for the MetaModels extension. + + + As the developers of this project, we receive no compensation for our work. + Much of our valuable time is freely given to the MetaModels project. Any + donations towards the MetaModels project would allow us to work on the + project during our working hours, speeding up development significantly. + <br /><br /> We guarantee to spend any donations only on this project. + If you have any specific requirements or features requests, you are free + to hire a member from our <a target="_blank" + href="https://now.metamodel.me/en/about/team">team</a>, who will gladly + help you.<br /><br />A big thanks to all our <a target="_blank" + href="https://now.metamodel.me/en/supporters">existing supporters and + sponsors</a>. Please consider joining our list of sponsors. We could not + create such a popular open source project without the support of the + community.<br /><br /><a target="_blank" + href="https://now.metamodel.me/en/supporters/donate">More informations</a> + + + + We are calling for your help! + + + Thanks to these users for tickets, suggestions and translations + + + Purpose + + + For donations check also our + <a target="_blank" href="https://now.metamodel.me/en/supporters/fundraising"> + fundraisings</a> or consider + <a target="_blank" href="https://now.metamodel.me/en/supporters/donate"> + donating for the manual</a>. + + + + Help for your MetaModels projects + + + On our <a target="_blank" href="https://now.metamodel.me/en/help">help + website as guide to MetaModels</a> you can find the manual, all support + channels, videos tutorials and our newsletter service.<br /><br /> + <a target="_blank" href="https://metamodels.readthedocs.io/en/latest/"> + Direct link to the MetaModels manual...</a> + + + + See contributors at github... + + + + diff --git a/src/CoreBundle/Resources/translations/metamodels_wildcard.en.xlf b/src/CoreBundle/Resources/translations/metamodels_wildcard.en.xlf new file mode 100644 index 000000000..ed5fabc8a --- /dev/null +++ b/src/CoreBundle/Resources/translations/metamodels_wildcard.en.xlf @@ -0,0 +1,41 @@ + + + + + + MM + + + MetaModel + + + Fi + + + Filter + + + Rs + + + Rendersetting + + + MetaModels elements + + + MetaModels list + + + MetaModels list + + + MetaModels frontend filter + + + MetaModels clear all + + + + diff --git a/src/CoreBundle/Resources/translations/modules.en.xlf b/src/CoreBundle/Resources/translations/modules.en.xlf new file mode 100644 index 000000000..b7f0c549a --- /dev/null +++ b/src/CoreBundle/Resources/translations/modules.en.xlf @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/CoreBundle/Resources/translations/tl_content.en.xlf b/src/CoreBundle/Resources/translations/tl_content.en.xlf new file mode 100644 index 000000000..d179d01c0 --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_content.en.xlf @@ -0,0 +1,25 @@ + + + + + + Edit MetaModel + + + Edit the MetaModel ID %id%. + + + Edit render setting + + + Edit the render setting ID %id%. + + + Edit filter setting + + + Edit the filter setting ID %id%. + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel.en.xlf new file mode 100644 index 000000000..0c3471345 --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel.en.xlf @@ -0,0 +1,243 @@ + + + + + + All MetaModels + + + New MetaModel + + + Create a new MetaModel + + + Edit multiple + + + Edit multiple elements at once + + + Create a new element at the top + + + Create a new element after element ID %id% + + + Paste at the top + + + Paste after element ID %id% + + + Go back + + + Back to the previous page + + + Show the details of record %id% + + + Id + + + Id of the MetaModel + + + Name + + + MetaModel name. + + + Revision date + + + Date and time of the latest revision + + + Table name + + + Name of database table to store items to. + + + Parent list mode + + + Mode to use for parent/child relationship. + + + Translation + + + Check if this MetaModel shall support translation/multilingualism. + + + Languages to provide for translation + + + Specify all languages that shall be available for translation. + + + Language + + + Select the languages you want to provide. + + + Fallback language + + + Check the language that shall be used as fallback. + + + Variant support + + + Check if this MetaModel shall support variants of items. + + + Locale territory support + + + Check if this MetaModel shall support language territory at locale. + + + Sorting + + + Sorting order of items. + + + Name and table + + + Translation + + + Advanced settings + + + Cancel + + + Cancel multiple processing. + + + Manage items + + + Manage items of MetaModel ID %id% + + + Copy MetaModel definition + + + Copy definition of MetaModel ID %id% + + + Delete MetaModel + + + Delete MetaModel ID %id% + + + Do you really want to delete element ID %id%? + + + MetaModel details + + + Show details of MetaModel ID %id% + + + Edit MetaModel + + + Edit the MetaModel ID %id% + + + Define attributes + + + Define attributes for MetaModel ID %id% + + + Define filters + + + Define filters for MetaModel ID %id% + + + Define render settings + + + Define render settings for MetaModel ID %id% + + + Define input screens + + + Define input screens for MetaModel ID %id% + + + Define input/output combinations + + + Define input/output combinations for MetaModel ID %id% + + + Move MetaModel + + + Define the order of your MetaModels. + + + Define search settings + + + Define search settings for MetaModel ID %id% + + + {-1} Table does not exist, run migration!| {0} No items|{1} 1 item|[2,Inf[ %count% items + + + Do you really want to delete MetaModel ID %id%? + + + After creating a model, the database must be migrated (console, Contao Manager) - even if the table + name is changed. When changing the table name, the user data itself must be transferred. + + + + Create a new item + + + Edit record %id% + + + Save + + + Save and close + + + Save and new + + + Save and go back + + + The table name is not given or empty. + + + The table name "%table_name%" is invalid. + + + Table "%table_name%" already exists. + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_attribute.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_attribute.en.xlf new file mode 100644 index 000000000..a1578ab14 --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_attribute.en.xlf @@ -0,0 +1,192 @@ + + + + + + All attributes + + + Id + + + Pid + + + Sorting + + + Revision date + + + New attribute + + + Create a new attribute + + + Edit multiple + + + Edit multiple elements at once + + + Create a new element at the top + + + Create a new element after element ID %id% + + + Paste at the top + + + Paste after element ID %id% + + + Go back + + + Back to the previous page + + + Attribute type + + + Select the type of this attribute. WARNING: if you change this, all existing data within this attribute + will be deleted. + + + + Name + + + Human readable name + + + Description + + + Human readable description + + + Column name + + + Internal reference name for this attribute + + + Enable variant override + + + Check this, if you want variants within the MetaModel to override the parent item's value + + + Unique values + + + Check this, if you want to ensure that each value only occurs once + + + Language + + + Description + + + Type, naming and base attribute configuration + + + Advanced settings + + + Edit attribute + + + Cancel + + + Cancel multiple processing. + + + Edit attribute ID %id% + + + Cut attribute definition + + + Cut definition of attribute ID %id% + + + Copy attribute definition + + + Copy definition of attribute ID %id% + + + Delete attribute + + + Delete attribute ID %id% + + + Do you really want to delete element ID %id%? + + + Attribute details + + + Show details of attribute ID %id% + + + Edit attribute + + + Edit the attribute + + + Unknown attribute! + + + Extension missing? The attribute type "%id%" is not installed. + + + Do you really want to delete attribute ID %id%? + + + After creating an attribute, the database must be migrated (console, Contao Manager) - even if the + column name is changed. When changing the column name, the user data itself must be transferred. + + + + Create a new item + + + Edit record %id% + + + Show the details of record %id% + + + Save + + + Save and close + + + Save and new + + + Save and go back + + + Column "%col_name%" already exists on table "%table_name%". + + + The column name "%col_name%" is invalid. + + + The column name "%col_name%" is reserved for system use. + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_dca.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_dca.en.xlf new file mode 100644 index 000000000..7ab65aa0d --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_dca.en.xlf @@ -0,0 +1,284 @@ + + + + + + All input screens + + + Id + + + Pid + + + Sorting + + + New input screen + + + Create a new input screen + + + Edit multiple + + + Edit multiple elements at once + + + Create a new element at the top + + + Create a new element after element ID %id% + + + Paste at the top + + + Paste after element ID %id% + + + Save + + + Save and close + + + Save and new + + + Save and go back + + + Go back + + + Back to the previous page + + + Show the details of record %id% + + + Edit record %id% + + + Edit MetaModel + + + Edit the MetaModel ID %id% + + + Name + + + Name of the input screen. + + + Revision date + + + Date and time of the latest revision. + + + Integration + + + Select the desired type of integration. + + + Render mode + + + Select the desired render mode. + + + Parent table name + + + Name of the database table that shall be referred to as parent table. + + + Use column based layout + + + If selected a table header will be generated with column names. + + + Backend section + + + Select the desired backend section where you want the MetaModel appear. For models that shall be edited + by end users, the "content" section most likely will be appropriate. + + + + Backend icon + + + Select the desired backend icon.This icon will get used to draw an image in the parent list if you have + a integration as child table. + + + + Backend caption + + + The text you specify in here, will get used as the label and description text in the backend menu. + + + + Language + + + Select the languages you want to provide. + + + Label text + + + The text you specify in here, will get used as the menu label in the backend menu. + + + Description text + + + The text you specify in here, will get used as the description (hover title) in the backend menu. + + + + Panel layout + + + Separate panel options with comma (= space) and semicolon (= new line) like + "filter;search;sort,limit". + + + + Panelpicker + + + View limitation + + + Activate the view limitation. + + + Limit the render setting + + + Choose between front end or backend. + + + Allow editing of items + + + If checked, this input screen allows the editing of items. + + + Allow creating of items + + + If checked, this input screen allows the creating of items. + + + Allow deleting of items + + + If checked, this input screen allows the deleting of items. + + + Name + + + View settings + + + Backend integration + + + Data display settings + + + Data manipulation permissions + + + Edit input screen + + + Edit the input screen ID %id% + + + Copy input screen definition + + + Copy definition of input screen ID %id% + + + Delete input screen + + + Delete input screen ID %id% + + + Do you really want to delete element ID %id%? + + + Input screen details + + + Show details of input screen ID %id% + + + Input screen settings + + + Edit the settings of input screen ID %id% + + + Grouping and sorting + + + Edit the grouping and sorting settings of input screen ID %id% + + + Additions to the mask-heading + + + The field can be used to adapt the heading of the mask at edit and replaces the output of the ID.You can + use simple tokens e.g. ##model_name##, ##model_firstname## [##model_id##] + + + + Standalone + + + As child table + + + Flat + + + Parented + + + Hierarchical + + + When using the "Hierarchical" render mode, the current sort must be enable to "Manual sorting" as + default. + + + + Do you really want to delete input screen ID %id%? + + + Cancel + + + Cancel multiple processing. + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_dca_combine.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_dca_combine.en.xlf new file mode 100644 index 000000000..4e9fe501c --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_dca_combine.en.xlf @@ -0,0 +1,73 @@ + + + + + + All input/output combinations + + + Save + + + Save and close + + + Save and new + + + Save and go back + + + Go back + + + Back to the previous page + + + Edit record %id% + + + Combination configuration + + + Permissions for input screen and views + + + For selected frontend user group (if any) and selected backend user group (if any) use the selected + palette and the selected view. + + + + Frontend group + + + The frontend user group the combination applies to; * is 'catch all'. + + + Backend group + + + The backend user group the combination applies to; * is 'catch all'. + + + Input screen + + + The input screen the combination applies to. + + + Render setting + + + The view the combination applies to. + + + Administrator + + + Anonymous + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_dca_sortgroup.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_dca_sortgroup.en.xlf new file mode 100644 index 000000000..77d4b05fe --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_dca_sortgroup.en.xlf @@ -0,0 +1,231 @@ + + + + + + All grouping and sorting settings + + + Id + + + Pid + + + Revision date + + + Sorting + + + Published + + + New Sorting and grouping + + + Create a new Sorting and grouping + + + Edit multiple + + + Edit multiple elements at once + + + Create a new element at the top + + + Create a new element after element ID %id% + + + Paste at the top + + + Paste after element ID %id% + + + Go back + + + Back to the previous page + + + Save + + + Save and close + + + Save and new + + + Save and go back + + + Edit record %id% + + + Name + + + Name of the sorting group. + + + Is default + + + Determines that this input screen shall be used as default for the parenting MetaModel. + + + Render mode + + + Select the desired render mode. + + + Language + + + Select the languages you want to provide. + + + Label text + + + The text you specify in here, will get used as the menu label in the backend menu. + + + Description text + + + The text you specify in here, will get used as the description (hover title) in the backend menu. + + + + Grouping type + + + The grouping type to use in the item view. + + + Grouping attribute + + + The attribute to use for grouping in the item view. + + + Grouping length + + + The amount of characters to use for grouping. + + + Sorting attribute + + + The attribute to sort by. + + + Sorting direction + + + The sorting direction. + + + Enable manual sorting + + + If this is enabled, the user will be able to perform manual sorting. + + + Name + + + Data display settings + + + Edit definition + + + Edit the definition ID %id% + + + Copy definition + + + Copy the definition ID %id% + + + Delete definition + + + Delete the definition ID %id% + + + Do you really want to delete element ID %id%? + + + Definition details + + + Show details of definition ID %id% + + + Toggle + + + Toggle the state of definition ID %id% + + + Definition settings + + + Edit the settings of definition ID %id% + + + Do not group + + + Group by initial letter(s) + + + Group by numeric order + + + Group by day of date + + + Group by weekday of date + + + Group by week of year + + + Group by month of date + + + Group by year of date + + + Ascending + + + Descending + + + Do you really want to delete definition ID %id%? + + + Show the details of record %id% + + + Cancel + + + Cancel multiple processing. + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_dcasetting.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_dcasetting.en.xlf new file mode 100644 index 000000000..fca38de04 --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_dcasetting.en.xlf @@ -0,0 +1,335 @@ + + + + + + All input screen settings + + + Id + + + Pid + + + Sorting + + + Revision date + + + Published + + + New attribute or legend + + + Add a new attribute or legend for input mask + + + Edit multiple + + + Edit multiple elements at once + + + Create a new element at the top + + + Create a new element after element ID %id% + + + Paste at the top + + + Paste after element ID %id% + + + Go back + + + Back to the previous page + + + Save and close + + + Continue + + + Select all + + + Cancel + + + Cancel multiple processing. + + + Type + + + Select the attribute type. + + + Attribute + + + Attribute this setting relates to. + + + Custom template to use for generating + + + Select the template that shall be used for the selected attribute. Valid template files start with "mm_&lt;type&gt;" + where the type name is put for &lt;type&gt; + + + + Backend class + + + Here you can set backend class(es). Open the wizard for an overview of the classes. + + + Template at backend + + + You can chose own template for widget in backend - add file in root folder of template. + + + Collapse section + + + Collapse the section by default. + + + Legend title + + + Here you can enter the legend title. + + + Language + + + Legend title + + + Mandatory + + + Check if this attribute shall be mandatory. + <br />NOTE: This will be implicitely active on if you selected "Unique values" in the attribute + configuration. + + + + Always save + + + If true the field will always be saved, even if its value has not changed. + + + Chosen + + + Enable Chosen graphical select widget. + + + Filterable + + + Check if this attribute shall be available for backend filtering. + + + Searchable + + + Check if this attribute shall be available for backend search. + + + Do not encode allowed html tags. + + + If you select this, allowed HTML tags from system settings will not be encoded. + + + Do not encode all html tags. + + + If you select this, no HTML tags will be encoded. + + + Decode HTML entities. + + + If you select this, all HTML entities will be decoded. Note that HTML entities are always decoded if "Do + not encode allowed html tags" is true. + + + + Enable richtext editor on this + + + Select the rich text configuration that shall be used on this field (if any). + + + Rows + + + Amount of rows to use for longtext/table widget. + + + Columns + + + Amount of colums to use for longtext/table widget + + + Trailing slash handling + + + Here you can specify how trailing slashes shall be handled + + + Replace spaces with underscore + + + If true any whitespace character will be replaced by an underscore. + + + Include blank option + + + if true a blank option will be added to the options which allows to define a &quot;no item selected&quot; + option. + + + + Read only + + + If true a the widget will be read only and may not be changed. + + + Submit on change + + + If active the form will be submitted when the field value changes. + + + Type + + + Widget appearance related options + + + Functionality related options + + + Filtering and searching in the backend list + + + Backend + + + Configuration + + + Advanced + + + Edit setting + + + Edit the setting ID %id% + + + Cut setting definition + + + Cut the setting ID %id% + + + Copy setting definition + + + Copy the setting ID %id% + + + Delete setting + + + Delete the setting ID %id% + + + Do you really want to delete element ID %id%? + + + Manage visibility conditions + + + Manage the visibility conditions of property ID %id% + + + Setting details + + + Show details of setting ID %id% + + + Toggle + + + Toggle the state of setting ID %id% + + + Add all + + + Add all attributes to input screen + + + Legend + + + Attribute + + + Strip slash on save + + + Add slash on save + + + Do nothing + + + Will add the attribute "%name%" [%type%, "%colName%"] to the input screen. + + + Attribute "%name%" [%type%, "%colName%"] is already contained in input screen. + + + Added the attribute "%name%" [%type%, "%colName%"] to the input screen. + + + Add new settings enabled. + + + Set backend class. + + + Unique attribues are automatically mandatory (this is not changable). + + + Attribues with force alias are automatically readonly (this is not changable). + + + Show the details of record %id% + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_dcasetting_condition.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_dcasetting_condition.en.xlf new file mode 100644 index 000000000..efbcc4020 --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_dcasetting_condition.en.xlf @@ -0,0 +1,192 @@ + + + + + + All input screen conditions + + + Id + + + Pid + + + Fid + + + Sorting + + + Revision date + + + New condition + + + Add a new visibility condition + + + Edit multiple + + + Edit multiple elements at once + + + Go back + + + Back to the previous page + + + Visibility conditons + + + Basic configuration + + + Condition configuration + + + Type + + + Select the condition type. + + + Enabled + + + Check to enable this condition. + + + Comment + + + Enter a comment to describe the purpose of this condition. + + + Attribute + + + Select the attribute to use for this condition. + + + Value + + + Please select the desired value. + + + Edit setting + + + Edit the setting ID %id% + + + Cut setting definition + + + Cut the setting ID %id% + + + Copy setting definition + + + Copy the setting ID %id% + + + Delete setting + + + Delete the setting ID %id% + + + Do you really want to delete element ID %id%? + + + Manage conditions + + + Manage the conditions of property ID %id% + + + Setting details + + + Show details of setting ID %id% + + + Toggle + + + Toggle the state of setting ID %id% + + + Add all + + + Add all attributes to input screen + + + Add new at the top + + + Add new after setting ID %id% + + + Create new after setting ID %id% + + + Create new after setting ID %id% + + + Create new setting at the top + + + Create new at the top of setting ID %id% + + + Attribute value is... + + + Attribute values contain any of... + + + Is attribute visible... + + + OR + + + AND + + + NOT + + + %icon% <strong>%name%</strong> - for attribute <em>%attribute%</em> (Parameter: %value%)%comment% + + + + %icon% <strong>%name%</strong> - any sub conditions must be fulfilled%comment% + + + %icon% <strong>%name%</strong> - all sub conditions must be fulfilled%comment% + + + %icon% <strong>%name%</strong> - invert the result of the contained condition%comment% + + + %icon% <strong>%attribute%</strong> value is %value%%comment% + + + Do you really want to delete setting ID %id%? + + + Show the details of record %id% + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_filter.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_filter.en.xlf new file mode 100644 index 000000000..11b2236d3 --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_filter.en.xlf @@ -0,0 +1,128 @@ + + + + + + All filters + + + Id + + + Pid + + + Save + + + Save and close + + + Save and new + + + Save and go back + + + Edit record %id% + + + Show the details of record %id% + + + New filter + + + Create a new filter + + + Edit multiple + + + Edit multiple elements at once + + + Create a new element at the top + + + Create a new element after element ID %id% + + + Paste at the top + + + Paste after element ID %id% + + + Go back + + + Back to the previous page + + + Name + + + Filter setting name. + + + Revision date + + + Date and time of the latest revision. + + + Name + + + Edit + + + Edit MetaModel + + + Edit setting + + + Edit the filter setting ID %id% + + + Copy filter setting + + + Copy the filter setting ID %id% + + + Delete filter setting + + + Delete the filter setting ID %id% + + + Do you really want to delete element ID %id%? + + + Filter setting details + + + Show details of filter setting ID %id% + + + Define attribute settings + + + Define attribute settings for filter setting ID %id% + + + Do you really want to delete filter setting ID %id%? + + + Cancel + + + Cancel multiple processing. + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_filtersetting.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_filtersetting.en.xlf new file mode 100644 index 000000000..e80af871a --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_filtersetting.en.xlf @@ -0,0 +1,339 @@ + + + + + + All filter settings + + + Id + + + Pid + + + Sorting + + + Revision date + + + New filter setting + + + Create a new filter setting + + + Edit multiple + + + Edit multiple elements at once + + + Create a new element at the top + + + Create a new element after element ID %id% + + + Paste at the top + + + Paste after element ID %id% + + + Create new setting at the top + + + Create new into setting ID %id% + + + Go back + + + Back to the previous page + + + Filter settings + + + Parent collection + + + The collection of filter settings, this setting belongs to. + + + Type + + + The type of this setting. + + + Enabled + + + Enable this filter setting. + + + Comment + + + A short comment for describing the purpose of this filter setting. + + + Attribute + + + Attribute this setting relates to. + + + Search all languages + + + Check if you want to perform the lookup language independant. If this is not checked, only the current + active language will be searched. + + + + Allow empty value + + + Check if you want to allow this filter value to be emtpy, if checked and the parameter holds an empty + value, this filter rule will behave as if it was not defined. + + + + Stop after first match + + + Check if you want this filter setting to stop executing its child rules after the first subset returned + matches. + + + + URL parameter + + + The URL parameter that shall get mapped to the selected attribute. The special <em>"auto_item"</em> + parameter can also be used, this is especially useful for alias columns. + + + + Static parameter + + + Check if you want to be able to set the value of this parameter in the parenting list (modules, content + elements, etc.). + + + + Provide Frontend widget + + + Check if you want to display a filter widget in the Frontend. + + + Custom SQL Query + + + The SQL query that shall be executed, insert tags are supported. + + + Label + + + Show label instead of attribute name. + + + Template + + + Sub template for this filter element. Standard: form widget. + + + Empty option + + + Show empty options in filter widget. + + + Assigned values only + + + Show only options, that are assigned somewhere in the MetaModel. + + + Remaining values only + + + Show only options, that are still assigned somewhere after the actual filter is set. + + + Ignore this filter for the remaining values + + + If activate the filter will return all options without itself in the filter rules. + + + Hide label in filter widget + + + If active, the label is not output. + + + Use label as blank option + + + If active, the label output as blank option. + + + Sorting + + + Please enter sorting of values in filter widget. + + + CSS ID/class + + + Here you can set an ID and one or more classes. + + + Placeholder + + + Show this text as long as the field is empty. + + + Natural sorting (ASC) + + + Natural sorting (DESC) + + + Default + + + Default value for selection. + + + Type + + + Configuration + + + Frontend filter + + + Edit setting + + + Edit the filter setting ID %id% + + + Copy filter setting definition + + + Copy the filter setting ID %id% + + + Cut filter setting definition + + + Cut the filter setting ID %id% + + + Delete filter setting + + + Delete the filter setting ID %id% + + + Do you really want to delete element ID %id%? + + + Filter setting details + + + Show details of filter setting ID %id% + + + Toggle + + + Toggle the state of setting ID %id% + + + Predefined set of items + + + Simple lookup + + + Custom SQL + + + OR condition + + + AND condition + + + <em>[%colName%, "%name%"]</em> + + + <em>(URL: %urlparam%)</em> + + + <br />%comment% + + + %1$s <strong>%2$s</strong> %3$s %5$s %4$s + + + %1$s <strong>%2$s</strong> %3$s %5$s %4$s + + + %1$s <strong>%2$s</strong> %3$s %4$s + + + %1$s <strong>%2$s</strong> %4$s + + + %1$s <strong>%2$s</strong> %4$s + + + Items + + + Please enter the IDs of the items for filtering as comma-separated list. + + + Do you really want to delete filter setting ID %id%? + + + Cancel + + + Cancel multiple processing. + + + Edit record %id% + + + Save + + + Save and close + + + Save and new + + + Save and go back + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_item.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_item.en.xlf new file mode 100644 index 000000000..56e1fb534 --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_item.en.xlf @@ -0,0 +1,150 @@ + + + + + + New item + + + Create new item + + + Edit item + + + Edit item ID %id% + + + Edit multiple + + + Edit multiple + + + Edit "%child_name%" + + + Edit "%child_name%" for item %id% + + + Copy item + + + Copy item ID %id% + + + New variant + + + Create a new variant of item ID %id% + + + Move item + + + Move item ID %id% + + + Delete item + + + Delete item ID %id% + + + Do you really want to delete element ID %id%? + + + Item details + + + Show details of item ID %id% + + + Edit item type + + + Edit the item type + + + Create new item + + + Create a new item after item ID %id% + + + Create new item + + + Create a new item in item ID %id% + + + Add new at the top + + + Add new item after item ID %id% + + + Is variant base + + + Check this if you want to make this the base for the current variant group + + + Edit item %item% + + + Create a new item + + + Sorting + + + The manual sorting + + + ID of the item + + + The id of the item + + + Parent item id + + + The id of the parent item + + + Revision date + + + The date when the item revision was created + + + Details + Used in the frontend for the "Details" link caption. + + + Show the details of record %id% + + + Go back + + + Back to the previous page + + + Save + + + Save and close + + + Save and new + + + Save and go back + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_rendersetting.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_rendersetting.en.xlf new file mode 100644 index 000000000..0bd355579 --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_rendersetting.en.xlf @@ -0,0 +1,178 @@ + + + + + + All render setting settings + + + Id + + + Pid + + + Revision date + + + Sorting + + + New attribute + + + Add a new attribute for render setting + + + Edit multiple + + + Edit multiple elements at once + + + Create a new element at the top + + + Create a new element after element ID %id% + + + Paste at the top + + + Paste after element ID %id% + + + Go back + + + Back to the previous page + + + Continue + + + Select all + + + Save + + + Save and close + + + Save and new + + + Save and go back + + + Edit record %id% + + + Attribute + + + Attribute this setting relates to. + + + Custom template to use for generating + + + Select the template that shall be used for the selected attribute. Valid template files start with "mm_&lt;type&gt;" + where the type name is put for &lt;type&gt; + + + + Custom CSS class + + + Enter any CSS classes that you want get added to the output of this attribute + + + Type + + + Advanced + + + Edit setting + + + Edit render setting ID %id% + + + Cut render setting definition + + + Cut the render setting ID %id% + + + Copy render setting definition + + + Copy the render setting ID %id% + + + Delete render setting + + + Delete the render setting ID %id% + + + Do you really want to delete element ID %id%? + + + Render setting details + + + Show details of render setting ID %id% + + + Toggle + + + Toggle the state of render setting ID %id% + + + Add all + + + Add all attributes to render setting + + + %id% <strong>%id%</strong> <em>[%id%]</em> + + + Will add the attribute "%name%" [%type%, "%colName%"] to the input screen. + + + Attribute "%name%" [%type%, "%colName%"] is already contained in input screen. + + + Added the attribute "%name%" [%type%, "%colName%"] to the input screen. + + + Add new settings enabled. + + + Do you really want to render filter setting ID %id%? + + + Enabled + + + Enable this filter setting. + + + Show the details of record %id% + + + Cancel + + + Cancel multiple processing. + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_rendersettings.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_rendersettings.en.xlf new file mode 100644 index 000000000..9649e9518 --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_rendersettings.en.xlf @@ -0,0 +1,225 @@ + + + + + + All render settings + + + Id + + + Pid + + + Revision date + + + New render setting + + + Create a new render setting + + + Edit multiple + + + Edit multiple elements at once + + + Create a new element at the top + + + Create a new element after element ID %id% + + + Paste at the top + + + Paste after element ID %id% + + + Go back + + + Back to the previous page + + + Edit MetaModel + + + Edit the MetaModel ID %id% + + + Name + + + General settings + + + Expert settings + + + Frontend jump-to settings + + + Additional files + + + Name + + + Setting name. + + + Template + + + The template to use to render the items. + + + Output format + + + Define the output format. Leave empty to use the format used by current page. + + + HTML5 + + + Text + + + JumpTo page + + + The page that shall be used as "show details" urls. Note that the detailed URL params will get generated + by the filter setting that is currently in use. + + + + All languages + + + Language + + + The language for the jump to page. + + + Jump to page + + + The page to use for detail links. + + + Filter settings + + + The filter settings that define how the target (the reader/lister on the detail page) will filter the + items. + + + + Hide empty values + + + Hide empty values in backend and frontend. + + + Hide labels + + + Hide all labels in backend and frontend. + + + Additional css files + + + Choose this, if you want to include additional css files. + + + Additional javascript files + + + Choose this, if you want to include additional javascript files. + + + File + + + Choose a file + + + Publish + + + Check to publish the file. + + + Edit setting + + + Edit the setting ID %id% + + + Copy setting definition + + + Copy the setting ID %id% + + + Delete setting + + + Delete the setting ID %id% + + + Do you really want to delete element ID %id%? + + + Filter details + + + Show details of setting ID %id% + + + Define attribute settings + + + Define attribute settings for setting ID %id% + + + Unknown ID: %id% + + + unknown attribute + + + Unknown column + + + Do you really want to render setting ID %id%? + + + Show the details of record %id% + + + Edit record %id% + + + Save + + + Save and close + + + Save and new + + + Save and go back + + + + diff --git a/src/CoreBundle/Resources/translations/tl_metamodel_searchable_pages.en.xlf b/src/CoreBundle/Resources/translations/tl_metamodel_searchable_pages.en.xlf new file mode 100644 index 000000000..c4384c0da --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_metamodel_searchable_pages.en.xlf @@ -0,0 +1,140 @@ + + + + + + All search settings + + + Id + + + Pid + + + New index + + + Create a new searchable page setting + + + Edit multiple + + + Edit multiple elements at once + + + Create a new element at the top + + + Create a new element after element ID %id% + + + Paste at the top + + + Paste after element ID %id% + + + Go back + + + Back to the previous page + + + Show the details of record %id% + + + Name + + + Name of the searchable page setting + + + Revision date + + + Date and time of the latest revision. + + + Rendersetting + + + Choose the rendersetting which will be used for the search rendering. + + + Filtersetting + + + Choose the filtersetting which will be used for the search rendering. + + + Filter parameter override + + + Filter parameter override + + + Name + + + General settings + + + Edit searchable page + + + Edit the searchable page setting ID %id% + + + Copy searchable page + + + Copy definition of searchable page setting ID %id% + + + Delete searchable page + + + Delete searchable page setting ID %id% + + + Do you really want to delete element ID %id%? + + + Searchable page setting details + + + Show details of searchable page setting ID %id% + + + Toggle + + + Toggle the state of searchable page setting ID %id% + + + Searchable page settings + + + Edit the settings of searchable page setting ID %id% + + + Searchable page settings + + + Published + + + Do you really want to render setting ID %id%? + + + Cancel + + + Cancel multiple processing. + + + + diff --git a/src/CoreBundle/Resources/translations/tl_modules.en.xlf b/src/CoreBundle/Resources/translations/tl_modules.en.xlf new file mode 100644 index 000000000..bc384cb5e --- /dev/null +++ b/src/CoreBundle/Resources/translations/tl_modules.en.xlf @@ -0,0 +1,25 @@ + + + + + + Edit MetaModel + + + Edit the MetaModel ID %id%. + + + Edit render setting + + + Edit the render setting ID %id%. + + + Edit filter setting + + + Edit the filter setting ID %id%. + + + + diff --git a/src/CoreBundle/Resources/views/Backend/add-all.html.twig b/src/CoreBundle/Resources/views/Backend/add-all.html.twig index 9005cd433..213bef4f9 100644 --- a/src/CoreBundle/Resources/views/Backend/add-all.html.twig +++ b/src/CoreBundle/Resources/views/Backend/add-all.html.twig @@ -12,7 +12,7 @@ {% block main %}
    diff --git a/src/CoreBundle/Resources/views/Backend/be_config.html.twig b/src/CoreBundle/Resources/views/Backend/be_config.html.twig new file mode 100644 index 000000000..40b0e1a14 --- /dev/null +++ b/src/CoreBundle/Resources/views/Backend/be_config.html.twig @@ -0,0 +1,15 @@ +{% extends "@MetaModelsCore/Backend/be_base.html.twig" %} + +{% block headline %} + {{ headline }} +{% endblock %} + +{% block error %} + {%- if error is defined -%} + {{- error -}} + {%- endif -%} +{% endblock %} + +{% block main %} +{{ body | raw }} +{% endblock %} diff --git a/src/CoreBundle/Sorter/AttributeSorter.php b/src/CoreBundle/Sorter/AttributeSorter.php new file mode 100644 index 000000000..74fdf7bf2 --- /dev/null +++ b/src/CoreBundle/Sorter/AttributeSorter.php @@ -0,0 +1,74 @@ + + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\Sorter; + +use MetaModels\Attribute\IAttribute; + +/** + * This sort attributes. + */ +final class AttributeSorter +{ + /** + * Sort by name. + * + * @param array $attributes The attributes. + * @param string $dir The sort direction. + * + * @return array + * + * @SuppressWarnings(PHPMD.ShortVariable) + */ + public function sortByName(array $attributes, string $dir = 'ASC'): array + { + if ('ASC' === \strtoupper($dir)) { + \usort($attributes, fn($a, $b) => $a->getName() <=> $b->getName()); + } + + if ('DESC' === \strtoupper($dir)) { + \usort($attributes, fn($a, $b) => $b->getName() <=> $a->getName()); + } + + return $attributes; + } + + /** + * Sort by column name. + * + * @param array $attributes The attributes. + * @param string $dir The sort direction. + * + * @return array + * + * @SuppressWarnings(PHPMD.ShortVariable) + */ + public function sortByColumnName(array $attributes, string $dir = 'ASC'): array + { + if ('ASC' === \strtoupper($dir)) { + \usort($attributes, fn($a, $b) => $a->getColName() <=> $b->getColName()); + } + + if ('DESC' === \strtoupper($dir)) { + \usort($attributes, fn($a, $b) => $b->getColName() <=> $a->getColName()); + } + + return $attributes; + } +} diff --git a/src/CoreBundle/Translator/MetaModelTranslationLoader.php b/src/CoreBundle/Translator/MetaModelTranslationLoader.php new file mode 100644 index 000000000..1dcc6eb34 --- /dev/null +++ b/src/CoreBundle/Translator/MetaModelTranslationLoader.php @@ -0,0 +1,260 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\Translator; + +use Doctrine\DBAL\Exception; +use MetaModels\Attribute\IInternal; +use MetaModels\IFactory; +use MetaModels\ITranslatedMetaModel; +use MetaModels\ViewCombination\InputScreenInformationBuilder; +use MetaModels\ViewCombination\ViewCombination; +use Symfony\Component\Translation\Exception\NotFoundResourceException; +use Symfony\Component\Translation\Loader\LoaderInterface; +use Symfony\Component\Translation\MessageCatalogue; +use Symfony\Component\Translation\TranslatorBagInterface; + +use function is_array; +use function sprintf; + +final class MetaModelTranslationLoader implements LoaderInterface +{ + /** + * The constructor. + * + * @param TranslatorBagInterface $baseTranslator The translator interface. + * @param IFactory $factory The factory. + * @param ViewCombination $viewCombination The view combination. + * @param InputScreenInformationBuilder $builder The input screen builder. + */ + public function __construct( + private readonly TranslatorBagInterface $baseTranslator, + private readonly IFactory $factory, + private readonly ViewCombination $viewCombination, + private readonly InputScreenInformationBuilder $builder, + ) { + } + + /** + * Load translation catalog. + * + * @param mixed $resource The resource. + * @param string $locale The locale. + * @param string $domain The domain + * + * @return MessageCatalogue + * @throws Exception + * + * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + */ + public function load($resource, string $locale, string $domain = 'messages'): MessageCatalogue + { + // Load tl_metamodel_item catalogue. + $base = $this->baseTranslator->getCatalogue($locale); + + $catalog = new MessageCatalogue($locale); + + foreach ($base->all('tl_metamodel_item') as $key => $value) { + $catalog->set($key, $value, $domain); + } + + $metaModel = $this->factory->getMetaModel($domain); + if (null === $metaModel) { + throw new NotFoundResourceException('Failed to load MetaModel: ' . $domain); + } + + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if ($metaModel instanceof ITranslatedMetaModel) { + $metaModel->selectLanguage($locale); + $mainLanguage = $metaModel->getMainLanguage(); + } elseif ($metaModel->isTranslated(false)) { + $mainLanguage = $metaModel->getFallbackLanguage() ?? 'en'; + } else { + // Untranslated MetaModel. + $mainLanguage = 'en'; + } + + // Attributes: + foreach ($metaModel->getAttributes() as $attribute) { + if ($attribute instanceof IInternal) { + continue; + } + $colName = $attribute->getColName(); + $name = $attribute->get('name') ?? $colName; + $description = $attribute->get('description') ?? ''; + $catalog->set( + sprintf('%1$s.label', $colName), + $this->extractLangString($name, $locale, $mainLanguage) ?? $colName, + $domain + ); + $catalog->set( + sprintf('%1$s.description', $colName), + $this->extractLangString($description, $locale, $mainLanguage) ?? '', + $domain + ); + } + + foreach ($this->builder->fetchAllInputScreensForTable($domain) as $inputScreen) { + $this->handleInputScreen($domain, $locale, $mainLanguage, $inputScreen, $catalog); + } + + // Check if we have some children - add child button translations then. + foreach ($this->viewCombination->getChildrenOf($domain) as $tableName => $inputScreen) { + $subMetaModel = $this->factory->getMetaModel($tableName); + if (null === $subMetaModel) { + continue; + } + $translationKey = 'metamodel_edit_as_child.' . $subMetaModel->getTableName(); + + if ( + 'metamodel_edit_as_child.label' !== + $baseValue = $catalog->get('metamodel_edit_as_child.label', $domain) + ) { + $catalog->set( + $translationKey . '.label', + strtr($baseValue, ['%child_name%' => $subMetaModel->getName()]), + $domain, + ); + } + if ( + 'metamodel_edit_as_child.description' !== + $baseValue = $catalog->get('metamodel_edit_as_child.description', $domain) + ) { + $catalog->set( + $translationKey . '.description', + strtr($baseValue, ['%child_name%' => $subMetaModel->getName()]), + $domain, + ); + } + + $this->setTranslationLabelAndDescription( + $domain, + $locale, + $mainLanguage, + $translationKey, + $inputScreen, + $catalog, + $tableName, + ); + } + + return $catalog; + } + + /** + * Handle input screen. + * + * @param string $domain The domain. + * @param string $locale The locale. + * @param string $mainLanguage The fallback language. + * @param array $inputScreen The input screen. + * @param MessageCatalogue $catalog The catalog. + * + * @return void + */ + private function handleInputScreen( + string $domain, + string $locale, + string $mainLanguage, + array $inputScreen, + MessageCatalogue $catalog + ): void { + $prefix = 'inputscreen.' . $inputScreen['meta']['id'] . '.'; + + foreach ($inputScreen['legends'] as $index => $legend) { + $value = $this->extractLangString($legend['label'], $locale, $mainLanguage) ?? ''; + // Suffix '_legend' due to EditMask in DcGeneral. + $catalog->set($prefix . $index . '_legend', $value, $domain); + } + + if ('standalone' === $inputScreen['meta']['rendertype']) { + $this->setTranslationLabelAndDescription( + $domain, + $locale, + $mainLanguage, + $prefix . 'menu', + $inputScreen, + $catalog, + $domain, + ); + } + } + + private function setTranslationLabelAndDescription( + string $domain, + string $locale, + string $mainLanguage, + string $prefix, + array $inputScreen, + MessageCatalogue $catalog, + string $headlineDomain + ): void { + $headlineKey = 'backend-module.' . $inputScreen['meta']['id'] . '.headline'; + $catalog->set($prefix . '.description', '', $domain); + if ('' !== $value = $this->extractLangString($inputScreen['description'], $locale, $mainLanguage) ?? '') { + $catalog->set($prefix . '.description', $value, $domain); + + $catalog->set($headlineKey, $value, $headlineDomain); + } + if ('' !== $value = $this->extractLangString($inputScreen['label'], $locale, $mainLanguage) ?? '') { + $catalog->set($prefix . '.label', $value, $domain); + if ($headlineKey === $catalog->get($headlineKey, $headlineDomain)) { + $catalog->set($headlineKey, $value, $headlineDomain); + } + } + } + + /** + * @param string|array $value The value. + * @param string $locale The locale. + * @param string $mainLocale The fallback language. + * + * @return string|null + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + */ + private function extractLangString(string|array $value, string $locale, string $mainLocale): ?string + { + if (!is_array($value)) { + return $value; + } + + $fallback = null; + foreach ($value as $langCode => $string) { + if ($locale === $langCode && '' !== $string) { + return $string; + } + if ($mainLocale === $langCode && '' !== $string) { + $fallback = $string; + } + } + + if ('en' === $locale && null === $fallback && (null !== $default = ($value[''] ?? null))) { + return $default; + } + + return $fallback; + } +} diff --git a/src/CoreBundle/Translator/MetaModelTranslatorConfigurator.php b/src/CoreBundle/Translator/MetaModelTranslatorConfigurator.php new file mode 100644 index 000000000..b70ed2490 --- /dev/null +++ b/src/CoreBundle/Translator/MetaModelTranslatorConfigurator.php @@ -0,0 +1,109 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\Translator; + +use MetaModels\IFactory; +use MetaModels\ITranslatedMetaModel; +use Psr\Cache\InvalidArgumentException; +use Symfony\Component\Translation\Translator as SymfonyTranslator; +use Symfony\Contracts\Cache\CacheInterface; + +/** @psalm-type TDomainList=iterable> */ +final class MetaModelTranslatorConfigurator +{ + /** @var callable|null */ + private $previous; + + /** + * The constructor. + * + * @param IFactory $factory The factory. + * @param CacheInterface $cache The cache. + * @param callable|null $previous The previous configurator. + */ + public function __construct( + private readonly IFactory $factory, + private readonly CacheInterface $cache, + $previous = null + ) { + if (null !== $previous && !is_callable($previous)) { + throw new \InvalidArgumentException('Passed value for previous must be callable or null'); + } + $this->previous = $previous; + } + + /** + * @param SymfonyTranslator $translator The translator. + * + * @return void + * + * @throws InvalidArgumentException + */ + public function __invoke(SymfonyTranslator $translator): void + { + // Apply previous configurator + if (null !== $this->previous) { + \call_user_func($this->previous, $translator); + } + + foreach ($this->fetchDomains() as $domain => $locales) { + foreach ($locales as $locale) { + $translator->addResource('metamodels', $domain, $locale, $domain); + } + } + } + + /** + * Obtain the domain names with their locales. + * + * @return TDomainList + * @throws InvalidArgumentException + */ + private function fetchDomains(): iterable + { + return $this->cache->get( + 'metamodels.translation-domains', + /** @return TDomainList */ + function (): iterable { + $result = []; + foreach ($this->factory->collectNames() as $metamodelName) { + $instance = $this->factory->getMetaModel($metamodelName); + if (!$instance instanceof ITranslatedMetaModel) { + $result[$metamodelName] = ['en']; + continue; + } + $locales = []; + foreach ($instance->getLanguages() as $language) { + $locales[] = $language; + } + // Fix: Always add 'en' to the language domains, even if user only set 'af_NA' by quick save. + if (!\in_array('en', $locales, true)) { + array_unshift($locales, 'en'); + } + + $result[$metamodelName] = $locales; + } + + return $result; + } + ); + } +} diff --git a/src/CoreBundle/Translator/Translator.php b/src/CoreBundle/Translator/Translator.php new file mode 100644 index 000000000..33592e5ec --- /dev/null +++ b/src/CoreBundle/Translator/Translator.php @@ -0,0 +1,71 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\CoreBundle\Translator; + +use Symfony\Component\Translation\MessageCatalogueInterface; +use Symfony\Component\Translation\TranslatorBagInterface; +use Symfony\Contracts\Translation\LocaleAwareInterface; +use Symfony\Contracts\Translation\TranslatorInterface; + +final class Translator implements TranslatorInterface, TranslatorBagInterface, LocaleAwareInterface +{ + private TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator; + + /** + * @internal Do not inherit from this class; decorate the "contao.translation.translator" service instead + */ + public function __construct(TranslatorInterface&TranslatorBagInterface&LocaleAwareInterface $translator) + { + $this->translator = $translator; + } + + /** + * {@inheritdoc} + * + * Gets the translation from Contao’s $GLOBALS['TL_LANG'] array if the message + * domain starts with "contao_". The locale parameter is ignored in this case. + */ + public function trans($id, array $parameters = [], $domain = null, $locale = null): string + { + // Cut off the contao_ prefix for mm_ domains as they are already loaded via symfony. + if (null !== $domain && 0 === strncmp($domain, 'contao_mm_', 10)) { + $domain = substr($domain, 7); + } + + // Forward to Contao translator + return $this->translator->trans($id, $parameters, $domain, $locale); + } + + public function setLocale($locale): void + { + $this->translator->setLocale($locale); + } + + public function getLocale(): string + { + return $this->translator->getLocale(); + } + + public function getCatalogue($locale = null): MessageCatalogueInterface + { + return $this->translator->getCatalogue($locale); + } +} diff --git a/src/DcGeneral/Data/Driver.php b/src/DcGeneral/Data/Driver.php index 90906d839..e429bff67 100644 --- a/src/DcGeneral/Data/Driver.php +++ b/src/DcGeneral/Data/Driver.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,7 +19,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,6 +28,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\CollectionInterface; use ContaoCommunityAlliance\DcGeneral\Data\ConfigInterface; +use ContaoCommunityAlliance\DcGeneral\Data\DataProviderInterface; use ContaoCommunityAlliance\DcGeneral\Data\DCGE; use ContaoCommunityAlliance\DcGeneral\Data\DefaultCollection; use ContaoCommunityAlliance\DcGeneral\Data\DefaultConfig; @@ -42,6 +43,7 @@ use MetaModels\Attribute\IComplex; use MetaModels\Attribute\ITranslated; use MetaModels\Filter\IFilter; +use MetaModels\Helper\LocaleUtil; use MetaModels\IItem; use MetaModels\IItems; use MetaModels\IMetaModel; @@ -53,45 +55,50 @@ * Data driver class for DC_General. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) - The interface is too complex, maybe split into traits. + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @psalm-suppress DeprecatedTrait */ class Driver implements MultiLanguageDataProviderInterface { + /** @psalm-suppress DeprecatedTrait */ use DriverBcLayerTrait; /** * Name of current table. * - * @var string + * @var string|null */ - protected $strTable = null; + protected string|null $strTable = null; /** * The MetaModel this DataContainer is working on. * - * @var IMetaModel + * @var IMetaModel|null */ - protected $metaModel = null; + protected IMetaModel|null $metaModel = null; /** * The event dispatcher to pass to items. * - * @var null|EventDispatcherInterface + * @var EventDispatcherInterface|null */ - private $dispatcher = null; + private EventDispatcherInterface|null $dispatcher = null; /** * The current active language. * * @var string */ - protected $strCurrentLanguage; + protected string $strCurrentLanguage = ''; /** * The database connection. * * @var Connection|null */ - private $connection; + private Connection|null $connection = null; /** * Set dispatcher. @@ -126,31 +133,33 @@ public function setConnection(Connection $connection) * * The given value may be either integer, string or an instance of Model * - * @param mixed $varItem Id or the model itself, to delete. + * @param mixed $item Id or the model itself, to delete. * * @return void * * @throws \RuntimeException When an unusable object has been passed. */ - public function delete($varItem) + public function delete($item) { + $metaModel = $this->getMetaModel(); + assert($metaModel instanceof IMetaModel); + // Determine the id. - if (is_object($varItem) && ($varItem instanceof Model)) { - $objModelItem = $varItem->getItem(); + if (\is_object($item) && ($item instanceof Model)) { + $objModelItem = $item->getItem(); } else { - $objModelItem = $this->getMetaModel()->findById($varItem); + $objModelItem = $metaModel->findById($item); } if ($objModelItem) { - $this->getMetaModel()->delete($objModelItem); + $metaModel->delete($objModelItem); } } /** * Save a new Version of a record. * - * @param ModelInterface $objModel The model to be saved. - * - * @param string $strUsername The username that creates the new version. + * @param ModelInterface $model The model to be saved. + * @param string $username The username that creates the new version. * * @return void * @@ -158,7 +167,7 @@ public function delete($varItem) * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function saveVersion(ModelInterface $objModel, $strUsername) + public function saveVersion(ModelInterface $model, $username) { throw new \RuntimeException('Versioning not supported in MetaModels so far.'); } @@ -167,10 +176,9 @@ public function saveVersion(ModelInterface $objModel, $strUsername) * Return a model based of the version information. * * @param mixed $mixID The ID of record. - * * @param mixed $mixVersion The ID of the version. * - * @return void + * @return never-return * * @throws \RuntimeException As this is currently unimplemented, an Exception is thrown. * @@ -185,7 +193,6 @@ public function getVersion($mixID, $mixVersion) * Set a version as active. * * @param mixed $mixID The ID of record. - * * @param mixed $mixVersion The ID of the version. * * @return void @@ -204,7 +211,7 @@ public function setVersionActive($mixID, $mixVersion) * * @param mixed $mixID The ID of the record. * - * @return void + * @return mixed * * @throws \RuntimeException As this is currently unimplemented, an Exception is thrown. * @@ -227,17 +234,24 @@ public function getActiveVersion($mixID) */ protected function setLanguage($language = '') { - $previousLanguage = \str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); - if (!empty($language)) { - $metaModel = $this->getMetaModel(); - if ($metaModel instanceof ITranslatedMetaModel) { - $metaModel->selectLanguage($language); - } + $metaModel = $this->getMetaModel(); + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + $previousLanguage = ($metaModel instanceof ITranslatedMetaModel) + ? $metaModel->getLanguage() + : LocaleUtil::formatAsLocale($GLOBALS['TL_LANGUAGE']); - $language = \str_replace('_', '-', $language); - if ($GLOBALS['TL_LANGUAGE'] !== $language) { - $GLOBALS['TL_LANGUAGE'] = $language; - } + if (empty($language)) { + return $previousLanguage; + } + + if ($metaModel instanceof ITranslatedMetaModel) { + $previousLanguage = $metaModel->selectLanguage($language); + } + + $language = LocaleUtil::formatAsLanguageTag($language); + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + if ($GLOBALS['TL_LANGUAGE'] !== $language) { + $GLOBALS['TL_LANGUAGE'] = $language; } return $previousLanguage; @@ -254,9 +268,10 @@ protected function getMetaModel() { if (!$this->metaModel) { if ($this->metaModel === null) { - throw new \RuntimeException('No MetaModel instance set for ' . $this->strTable); + throw new \RuntimeException('No MetaModel instance set for ' . (string) $this->strTable); } } + return $this->metaModel; } @@ -267,23 +282,23 @@ protected function getMetaModel() * * If the model shall be retrieved by filter, use $objConfig->setFilter() to populate the config with a filter. * - * @param ConfigInterface $objConfig The config to use. + * @param ConfigInterface $config The config to use. * * @return null|ModelInterface */ - public function fetch(ConfigInterface $objConfig) + public function fetch(ConfigInterface $config) { $backupLanguage = $this->setLanguage($currentLanguage = $this->getCurrentLanguage()); - if ($objConfig->getId() !== null) { - $modelId = $objConfig->getId(); + if ($config->getId() !== null) { + $modelId = $config->getId(); } else { - $filter = $this->prepareFilter($objConfig); - $ids = $this->getIdsFromFilter($filter, $objConfig); - $modelId = reset($ids); + $filter = $this->prepareFilter($config); + $ids = $this->getIdsFromFilter($filter, $config); + $modelId = \reset($ids); } - $objItem = $modelId ? $this->getMetaModel()->findById($modelId, $objConfig->getFields() ?: array()) : null; + $objItem = (null !== $modelId) ? $this->getMetaModel()->findById($modelId, $config->getFields() ?? []) : null; $this->setLanguage($backupLanguage); @@ -297,20 +312,20 @@ public function fetch(ConfigInterface $objConfig) /** * Set base config with source and other necessary parameter. * - * @param array $arrConfig The configuration to use. + * @param array $config The configuration to use. * * @return void * * @throws \RuntimeException When no source has been defined. */ - public function setBaseConfig(array $arrConfig) + public function setBaseConfig(array $config) { - if (!$arrConfig['source']) { + if (!$config['source']) { throw new \RuntimeException('Missing table name.'); } - $this->strTable = $arrConfig['source']; - $this->metaModel = $arrConfig['metaModel']; + $this->strTable = $config['source']; + $this->metaModel = $config['metaModel'] ?? null; } /** @@ -373,6 +388,7 @@ protected function prepareFilter(ConfigInterface $configuration) // @codingStandardsIgnoreEnd } $builder = new FilterBuilder($this->getMetaModel(), $configuration, $this->connection); + return $builder->build(); } @@ -381,26 +397,27 @@ protected function prepareFilter(ConfigInterface $configuration) * * @param ConfigInterface $config The configuration to be applied. * - * @return array + * @return array{string, string}|null */ protected function extractSorting($config) { $sorting = $config->getSorting(); + $sortBy = \key($sorting); + if (null === $sortBy) { + return null; + } + $sortDir = $sorting[$sortBy] ?? DCGE::MODEL_SORTING_ASC; - $sortBy = key($sorting); - $sortDir = current($sorting) ?: DCGE::MODEL_SORTING_ASC; - - return array($sortBy, $sortDir); + return [$sortBy, \strtoupper($sortDir)]; } /** * Fetch the ids via the given filter. * * @param IFilter $filter The filter. - * * @param ConfigInterface $config The configuration to be applied. * - * @return string[] + * @return list */ protected function getIdsFromFilter($filter, $config) { @@ -408,10 +425,10 @@ protected function getIdsFromFilter($filter, $config) return $this->getMetaModel()->getIdsFromFilter( $filter, - $sorting[0], + $sorting[0] ?? '', $config->getStart(), $config->getAmount(), - strtoupper($sorting[1]) + $sorting[1] ?? DCGE::MODEL_SORTING_ASC, ); } @@ -419,10 +436,9 @@ protected function getIdsFromFilter($filter, $config) * Fetch the items via the given filter. * * @param IFilter $filter The filter. - * * @param ConfigInterface $config The configuration to be applied. * - * @return IItems|IItem[] The collection of IItem instances that match the given filter. + * @return IItems The collection of IItem instances that match the given filter. */ protected function getItemsFromFilter($filter, $config) { @@ -430,33 +446,33 @@ protected function getItemsFromFilter($filter, $config) return $this->getMetaModel()->findByFilter( $filter, - $sorting[0], + $sorting[0] ?? '', $config->getStart(), $config->getAmount(), - strtoupper($sorting[1]), - $config->getFields() ?: array() + $sorting[1] ?? DCGE::MODEL_SORTING_ASC, + $config->getFields() ?? [] ); } /** * Fetch all records (optional filtered, sorted and limited). * - * @param ConfigInterface $objConfig The configuration to be applied. + * @param ConfigInterface $config The configuration to be applied. * - * @return CollectionInterface|ModelInterface[]|string[] + * @return CollectionInterface|list */ - public function fetchAll(ConfigInterface $objConfig) + public function fetchAll(ConfigInterface $config) { $backupLanguage = $this->setLanguage($this->getCurrentLanguage()); - $filter = $this->prepareFilter($objConfig); - if ($objConfig->getIdOnly()) { + $filter = $this->prepareFilter($config); + if ($config->getIdOnly()) { $this->setLanguage($backupLanguage); - return $this->getIdsFromFilter($filter, $objConfig); + return $this->getIdsFromFilter($filter, $config); } - $items = $this->getItemsFromFilter($filter, $objConfig); + $items = $this->getItemsFromFilter($filter, $config); $collection = $this->getEmptyCollection(); foreach ($items as $objItem) { $collection->push(new Model($objItem)); @@ -477,24 +493,26 @@ public function fetchAll(ConfigInterface $objConfig) * The only information being interpreted from the passed config object is the first property to fetch and the * filter definition. * - * @param ConfigInterface $objConfig The filter config options. + * @param ConfigInterface $config The filter config options. * * @return FilterOptionCollectionInterface * * @throws \RuntimeException If improper values have been passed (i.e. not exactly one field requested). */ - public function getFilterOptions(ConfigInterface $objConfig) + public function getFilterOptions(ConfigInterface $config) { - $arrProperties = $objConfig->getFields(); - if (count($arrProperties) <> 1) { + $arrProperties = $config->getFields(); + if (\count($arrProperties ?? []) <> 1) { throw new \RuntimeException('objConfig must contain exactly one property to be retrieved.'); } - $objFilter = $this->prepareFilter($objConfig); + $objFilter = $this->prepareFilter($config); $metaModel = $this->getMetaModel(); + assert($metaModel instanceof IMetaModel); + $arrValues = $metaModel - ->getAttributeOptions($arrProperties[0], $objFilter); + ->getAttributeOptions($arrProperties[0] ?? '', $objFilter); $objCollection = new DefaultFilterOptionCollection(); foreach ($arrValues as $strKey => $strValue) { @@ -507,63 +525,66 @@ public function getFilterOptions(ConfigInterface $objConfig) /** * Return the amount of total items (filtering may be used in the config). * - * @param ConfigInterface $objConfig The filter config options. + * @param ConfigInterface $config The filter config options. * * @return int */ - public function getCount(ConfigInterface $objConfig) + public function getCount(ConfigInterface $config) { - $objFilter = $this->prepareFilter($objConfig); - return $this->getMetaModel()->getCount($objFilter); + $objFilter = $this->prepareFilter($config); + $metaModel = $this->getMetaModel(); + assert($metaModel instanceof IMetaModel); + + return $metaModel->getCount($objFilter); } /** * Return a list with all versions for the model with the given Id. * - * @param mixed $mixID The ID of the row. - * - * @param boolean $blnOnlyActive If true, only active versions will get returned, if false all version will get - * returned. + * @param mixed $mixID The ID of the row. + * @param boolean $onlyActive If true, only active versions will get returned, if false all version will get + * returned. * * @return CollectionInterface * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ - public function getVersions($mixID, $blnOnlyActive = false) + public function getVersions($mixID, $onlyActive = false) { // No version support on MetaModels so far, sorry. - return null; + return new DefaultCollection(); } /** * Determine if a given value is unique within the metamodel. * - * @param string $strField The attribute name. - * - * @param mixed $varNew The value that shall be checked. - * - * @param int $intId The (optional) id of the item currently in scope - pass null for new items. + * @param string $field The attribute name. + * @param mixed $new The value that shall be checked. + * @param mixed $primaryId The (optional) id of the item currently in scope - pass null for new items. * * @return bool True if the values is not yet contained within the table, false otherwise. */ - public function isUniqueValue($strField, $varNew, $intId = null) + public function isUniqueValue($field, $new, $primaryId = null) { - $attribute = $this->getMetaModel()->getAttribute($strField); - if ($attribute) { + $model = $this->getMetaModel(); + assert($model instanceof IMetaModel); + $attribute = $model->getAttribute($field); + if (null !== $attribute) { $matchingIds = $this ->prepareFilter( $this->getEmptyConfig()->setFilter( - array( - array( + [ + [ 'operation' => '=', - 'property' => $attribute->getColName(), - 'value' => $varNew - ) - ) + 'property' => $attribute->getColName(), + 'value' => $new + ] + ] ) ) ->getMatchingIds(); - return (count($matchingIds) == 0) || ($matchingIds == array($intId)); + + return ([] === $matchingIds) || ([(string) $primaryId] === $matchingIds); } return false; @@ -574,20 +595,22 @@ public function isUniqueValue($strField, $varNew, $intId = null) * * This clears the given property in all items in the data provider to an empty value. * - * @param string $strField The field to reset. + * @param string $field The field to reset. * - * @return void + * @return never * * @throws \RuntimeException For invalid ids. */ - public function resetFallback($strField) + public function resetFallback($field) { // @codingStandardsIgnoreStart @trigger_error(__CLASS__ . '::' . __METHOD__ . ' is deprecated - handle resetting manually', E_USER_DEPRECATED); // @codingStandardsIgnoreEnd $metaModel = $this->getMetaModel(); - $attribute = $metaModel->getAttribute($strField); + assert($metaModel instanceof IMetaModel); + + $attribute = $metaModel->getAttribute($field); $ids = $metaModel->getIdsFromFilter(null); if ($attribute instanceof IComplex) { @@ -597,14 +620,14 @@ public function resetFallback($strField) $attribute->unsetValueFor($ids, $this->getCurrentLanguage()); } if ($attribute instanceof IAttribute) { - $data = array(); + $data = []; foreach ($ids as $id) { $data[$id] = null; } $attribute->setDataFor($data); } - throw new \RuntimeException('Unknown attribute or type ' . $strField); + throw new \RuntimeException('Unknown attribute or type ' . $field); } /** @@ -613,15 +636,14 @@ public function resetFallback($strField) * If the item does not have an Id yet, the save operation will add it as a new row to the database and * populate the Id of the model accordingly. * - * @param ModelInterface $objItem The model to save back. - * - * @param int|null $timestamp Optional the timestamp. + * @param ModelInterface $item The model to save back. + * @param int|null $timestamp Optional the timestamp. * * @return ModelInterface The passed model. * * @throws \RuntimeException When an incompatible item was passed, an Exception is being thrown. */ - public function save(ModelInterface $objItem, $timestamp = null) + public function save(ModelInterface $item, $timestamp = null) { if (null === $timestamp) { // @codingStandardsIgnoreStart @@ -632,14 +654,16 @@ public function save(ModelInterface $objItem, $timestamp = null) // @codingStandardsIgnoreEnd } - if ($objItem instanceof Model) { + if ($item instanceof Model) { $backupLanguage = $this->setLanguage($this->getCurrentLanguage()); - $objItem->getItem()->save($timestamp); + $mmItem = $item->getItem(); + assert($mmItem instanceof IItem); + $mmItem->save($timestamp); $this->setLanguage($backupLanguage); - return $objItem; + return $item; } throw new \RuntimeException('ERROR: incompatible object passed to GeneralDataMetaModel::save()'); @@ -648,15 +672,14 @@ public function save(ModelInterface $objItem, $timestamp = null) /** * Save a collection of items to the data provider. * - * @param CollectionInterface $objItems The collection containing all items to be saved. - * - * @param int $timestamp Optional the timestamp. + * @param CollectionInterface $items The collection containing all items to be saved. + * @param int|null $timestamp Optional the timestamp. * * @return void * * @throws \RuntimeException When an incompatible item was passed. */ - public function saveEach(CollectionInterface $objItems, $timestamp = 0) + public function saveEach(CollectionInterface $items, $timestamp = 0) { if (null === $timestamp) { // @codingStandardsIgnoreStart @@ -667,7 +690,7 @@ public function saveEach(CollectionInterface $objItems, $timestamp = 0) // @codingStandardsIgnoreEnd } - foreach ($objItems as $objItem) { + foreach ($items as $objItem) { $this->save($objItem, $timestamp); } } @@ -675,45 +698,47 @@ public function saveEach(CollectionInterface $objItems, $timestamp = 0) /** * Check if the attribute exists in the table and holds a value. * - * @param string $strField The name of the attribute that shall be tested. + * @param string $columnName The name of the attribute that shall be tested. * * @return boolean */ - public function fieldExists($strField) + public function fieldExists($columnName) { return !!( - in_array($strField, array('id', 'pid', 'tstamp', 'sorting')) - || $this->getMetaModel()->getAttribute($strField) + \in_array($columnName, ['id', 'pid', 'tstamp', 'sorting']) + || $this->getMetaModel()->getAttribute($columnName) ); } /** * Check if two models have the same values in all properties. * - * @param ModelInterface $objModel1 The first model to compare. - * - * @param ModelInterface $objModel2 The second model to compare. + * @param ModelInterface $firstModel The first model to compare. + * @param ModelInterface $secondModel The second model to compare. * * @return boolean True - If both models are same, false if not. * * @throws \InvalidArgumentException If not both models are compatible with this data provider. */ - public function sameModels($objModel1, $objModel2) + public function sameModels($firstModel, $secondModel) { - if (!($objModel1 instanceof Model && $objModel2 instanceof Model)) { + if (!($firstModel instanceof Model && $secondModel instanceof Model)) { throw new \InvalidArgumentException('Passed models are not valid.'); } - $objNative1 = $objModel1->getItem(); - $objNative2 = $objModel2->getItem(); + $objNative1 = $firstModel->getItem(); + assert($objNative1 instanceof IItem); + $objNative2 = $secondModel->getItem(); + assert($objNative2 instanceof IItem); if ($objNative1->getMetaModel() === $objNative2->getMetaModel()) { return true; } foreach ($objNative1->getMetaModel()->getAttributes() as $objAttribute) { - if ($objNative1->get($objAttribute->getColName()) != $objNative2->get($objAttribute->getColName())) { + if ($objNative1->get($objAttribute->getColName()) !== $objNative2->get($objAttribute->getColName())) { return false; } } + return true; } @@ -726,11 +751,9 @@ public function sameModels($objModel1, $objModel2) */ public function createVariant(ConfigInterface $objConfig) { - $objItem = $this->getMetaModel()->findById($objConfig->getId())->varCopy(); - - if (!$objItem) { - return null; - } + $item = $this->getMetaModel()->findById($objConfig->getId()); + assert($item instanceof IItem); + $objItem = $item->varCopy(); $model = new Model($objItem); $model->setMeta($model::IS_CHANGED, true); @@ -749,17 +772,21 @@ public function getLanguages($mixID) if ($metaModel instanceof ITranslatedMetaModel) { $collection = new DefaultLanguageInformationCollection(); foreach ($metaModel->getLanguages() as $langCode) { - [$langCode, $country] = explode('_', $langCode, 2); - $collection->add(new DefaultLanguageInformation($langCode, $country ?: null)); + [$langCode, $locale] = \explode('_', $langCode, 2) + [null, null]; + $collection->add(new DefaultLanguageInformation($langCode, $locale)); } - if (count($collection) > 0) { + if (\count($collection) > 0) { return $collection; } return null; } - if (!$metaModel->isTranslated(false)) { + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if (!($metaModel instanceof ITranslatedMetaModel) && !$metaModel->isTranslated(false)) { return null; } @@ -777,10 +804,15 @@ public function getFallbackLanguage($mixID) { $metaModel = $this->getMetaModel(); if ($metaModel instanceof ITranslatedMetaModel) { - [$langCode, $country] = explode('_', $metaModel->getMainLanguage(), 2); - return new DefaultLanguageInformation($langCode, $country ?: null); + [$langCode, $locale] = \explode('_', $metaModel->getMainLanguage(), 2) + [null, null]; + return new DefaultLanguageInformation($langCode, $locale); } - if (!$metaModel->isTranslated(false)) { + + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if (!($metaModel instanceof ITranslatedMetaModel) && !$metaModel->isTranslated(false)) { return null; } @@ -792,13 +824,15 @@ public function getFallbackLanguage($mixID) /** * Set the current working language for the whole data provider. * - * @param string $strLanguage The new language, use short tag "2 chars like de, fr etc.". + * @param string $language The new language, use short tag "2 chars like de, fr etc.". * - * @return void + * @return DataProviderInterface */ - public function setCurrentLanguage($strLanguage) + public function setCurrentLanguage($language) { - $this->strCurrentLanguage = $strLanguage; + $this->strCurrentLanguage = $language; + + return $this; } /** @@ -808,6 +842,6 @@ public function setCurrentLanguage($strLanguage) */ public function getCurrentLanguage() { - return $this->strCurrentLanguage; + return '' === $this->strCurrentLanguage ? 'en' : $this->strCurrentLanguage; } } diff --git a/src/DcGeneral/Data/DriverBcLayerTrait.php b/src/DcGeneral/Data/DriverBcLayerTrait.php index 7599980da..951e01e47 100644 --- a/src/DcGeneral/Data/DriverBcLayerTrait.php +++ b/src/DcGeneral/Data/DriverBcLayerTrait.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Andreas Fischer - * @copyright 2012-2021 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -51,12 +52,13 @@ private function getLanguagesBcLayer($metaModel): ?DefaultLanguageInformationCol ); // @codingStandardsIgnoreEnd $collection = new DefaultLanguageInformationCollection(); - foreach ($metaModel->getAvailableLanguages() as $langCode) { - [$langCode, $country] = explode('_', $langCode, 2); - $collection->add(new DefaultLanguageInformation($langCode, $country ?: null)); + /** @psalm-suppress DeprecatedMethod */ + foreach ($metaModel->getAvailableLanguages() ?? [] as $langCode) { + [$langCode, $country] = \explode('_', $langCode ?: '', 2) + ['', null]; + $collection->add(new DefaultLanguageInformation($langCode, $country ?? null)); } - if (count($collection) > 0) { + if (\count($collection) > 0) { return $collection; } @@ -81,11 +83,12 @@ private function getFallbackLanguageBcLayer(IMetaModel $metaModel): DefaultLangu E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedMethod */ $langCode = $metaModel->getFallbackLanguage(); - [$langCode, $country] = explode('_', $langCode, 2); + [$langCode, $country] = \explode('_', $langCode ?? '', 2) + ['', null]; - return new DefaultLanguageInformation($langCode, $country ?: null); + return new DefaultLanguageInformation($langCode, $country ?? null); // @coverageIgnoreEnd } } diff --git a/src/DcGeneral/Data/FilterBuilder.php b/src/DcGeneral/Data/FilterBuilder.php index 2f4cce4d4..510be7cc6 100644 --- a/src/DcGeneral/Data/FilterBuilder.php +++ b/src/DcGeneral/Data/FilterBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author David Molineus * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -36,9 +36,20 @@ use MetaModels\Filter\Rules\SimpleQuery; use MetaModels\IMetaModel; use MetaModels\IMetaModelsServiceContainer; +use MetaModels\ITranslatedMetaModel; /** * Class to generate a MetaModels filter from a data configuration. + * + * @psalm-type TFilterANDOR=array{operation: 'AND'|'OR', children: list>} + * @psalm-type TFilterCMP=array{operation: "="|">"|"<", property: string, value: string|int|float} + * @psalm-type TFilterIN=array{operation: 'IN', property: string, values: list} + * @psalm-type TFilterLIKE=array{operation: 'LIKE', property: string, value: string} + * @psalm-type TFilterForProperty=TFilterCMP|TFilterIN|TFilterLIKE + * @psalm-type TFilter=TFilterANDOR|TFilterForProperty + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class FilterBuilder { @@ -67,9 +78,7 @@ class FilterBuilder * Generate a filter from a passed configuration. * * @param IMetaModel $metaModel The MetaModel. - * * @param ConfigInterface $configuration The data configuration. - * * @param Connection|null $connection The database connection. */ public function __construct(IMetaModel $metaModel, ConfigInterface $configuration, Connection $connection = null) @@ -77,18 +86,16 @@ public function __construct(IMetaModel $metaModel, ConfigInterface $configuratio $this->metaModel = $metaModel; $this->configuration = $configuration; - // @codingStandardsIgnoreStart - // @codeCoverageIgnoreStart if (null === $connection) { + // @codingStandardsIgnoreStart @trigger_error( 'Connection is missing. It has to be passed in the constructor. Fallback will be dropped.', E_USER_DEPRECATED ); + // @codingStandardsIgnoreEnd $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); } - // @codeCoverageIgnoreEnd - // @codingStandardsIgnoreEnd - $this->connection = $connection; } @@ -106,9 +113,12 @@ protected function getMetaModel() * Retrieve the service container. * * @return IMetaModelsServiceContainer + * + * @psalm-suppress DeprecatedInterface */ protected function getServiceContainer() { + /** @psalm-suppress DeprecatedMethod */ return $this->getMetaModel()->getServiceContainer(); } @@ -119,6 +129,7 @@ protected function getServiceContainer() */ protected function getDatabase() { + /** @psalm-suppress DeprecatedMethod */ return $this->getServiceContainer()->getDatabase(); } @@ -126,10 +137,8 @@ protected function getDatabase() * Build the sub query for a comparing operator like =,<,>. * * @param IAttribute $attribute The attribute. - * * @param IFilter $filter The filter to add the operations to. - * - * @param array $operation The operation to convert. + * @param TFilterCMP $operation The operation to convert. * * @return void * @@ -137,69 +146,39 @@ protected function getDatabase() */ private function getFilterForComparingOperator($attribute, IFilter $filter, $operation) { - if ($attribute) { - switch ($operation['operation']) { - case '=': - $filter->addFilterRule(new SearchAttribute( - $attribute, - $operation['value'], - $this->getMetaModel()->getAvailableLanguages() ?: array() - )); - return; - - case '>': - $filter->addFilterRule(new GreaterThan( - $attribute, - $operation['value'] - )); - return; - - case '<': - $filter->addFilterRule(new LessThan( - $attribute, - $operation['value'] - )); - return; - - default: - throw new \RuntimeException( - 'Error processing filter array - unknown operation ' . - var_export($operation['operation'], true), - 1 - ); - } - } + switch ($operation['operation']) { + case '=': + $filter->addFilterRule($this->buildSearchAttributeFilterRule($attribute, (string) $operation['value'])); + return; - $columns = $this->connection->getSchemaManager()->listTableColumns($this->getMetaModel()->getTableName()); - if ($columns[$operation['property']]) { - // System column? - $filter->addFilterRule(new SimpleQuery( - sprintf( - 'SELECT t.id FROM %s AS t WHERE t.%s %s?', - $this->getMetaModel()->getTableName(), - $operation['property'], - $operation['operation'] - ), - array($operation['value']), - 'id', - $this->connection - )); + case '>': + $filter->addFilterRule(new GreaterThan( + $attribute, + $operation['value'] + )); + return; - return; - } + case '<': + $filter->addFilterRule(new LessThan( + $attribute, + $operation['value'] + )); + return; - throw new \RuntimeException( - 'Error processing filter array - unknown property ' . var_export($operation['property'], true), - 1 - ); + default: + throw new \RuntimeException( + 'Error processing filter array - unknown operation ' . + \var_export($operation['operation'], true), + 1 + ); + } } /** * Return the filter query for a "foo IN ('a', 'b')" filter. * - * @param IFilter $filter The filter to add the operations to. - * - * @param array $operation The operation to convert. + * @param IFilter $filter The filter to add the operations to. + * @param TFilterIN $operation The operation to convert. * * @return void * @@ -208,18 +187,16 @@ private function getFilterForComparingOperator($attribute, IFilter $filter, $ope private function getFilterForInList(IFilter $filter, $operation) { // Rewrite the IN operation to a rephrased term: "(x=a) OR (x=b) OR ...". - $subRules = array(); + $subRules = []; foreach ($operation['values'] as $varValue) { - $subRules[] = array( + $subRules[] = [ 'property' => $operation['property'], 'operation' => '=', 'value' => $varValue - ); + ]; } - $this->calculateSubfilter(array( - 'operation' => 'OR', - 'children' => $subRules - ), $filter); + + $this->getAndOrFilter($filter, ['operation' => 'OR', 'children' => $subRules]); } /** @@ -227,11 +204,9 @@ private function getFilterForInList(IFilter $filter, $operation) * * The searched value may contain the wildcards '*' and '?' which will get converted to proper SQL. * - * @param IAttribute $attribute The attribute. - * - * @param IFilter $filter The filter to add the operations to. - * - * @param array $operation The operation to convert. + * @param IAttribute $attribute The attribute. + * @param IFilter $filter The filter to add the operations to. + * @param TFilterLIKE $operation The operation to convert. * * @return void * @@ -239,54 +214,24 @@ private function getFilterForInList(IFilter $filter, $operation) */ private function getFilterForLike($attribute, IFilter $filter, $operation) { - if ($attribute) { - $filter->addFilterRule(new SearchAttribute( - $attribute, - $operation['value'], - $this->getMetaModel()->getAvailableLanguages() ?: array() - )); - - return; - } - - $columns = $this->connection->getSchemaManager()->listTableColumns($this->getMetaModel()->getTableName()); - if ($columns[$operation['property']]) { - // System column? - $filter->addFilterRule(new SimpleQuery( - sprintf( - 'SELECT t.id FROM %s AS t WHERE t.%s LIKE ?', - $this->getMetaModel()->getTableName(), - $operation['property'] - ), - array($operation['value']), - 'id', - $this->connection - )); - - return; - } - - throw new \RuntimeException( - 'Error processing filter array - unknown property ' . var_export($operation['property'], true), - 1 - ); + $filter->addFilterRule($this->buildSearchAttributeFilterRule($attribute, $operation['value'])); } /** * Calculate a native SQL sub procedure. * * @param FilterBuilderSql $procedure The procedure to which to append to. - * * @param array $children The children to calculate. * * @return array */ - protected function buildNativeSqlProcedure(FilterBuilderSql $procedure, $children) + protected function buildNativeSqlProcedure(FilterBuilderSql $procedure, $children): array { - $skipped = array(); + $skipped = []; $metaModel = $this->getMetaModel(); $tableName = $metaModel->getTableName(); foreach ($children as $child) { + assert(\is_array($child)); // If there is an attribute contained within this rule, skip it. if (isset($child['property']) && $metaModel->hasAttribute($child['property'])) { $skipped[] = $child; @@ -296,14 +241,14 @@ protected function buildNativeSqlProcedure(FilterBuilderSql $procedure, $childre // Try to parse the sub procedure and extract as much as possible. if (('AND' === $child['operation']) || ('OR' === $child['operation'])) { - if (null === $child['children']) { + if (empty($child['children'])) { continue; } $subProcedure = new FilterBuilderSql($tableName, $child['operation'], $this->connection, 't.'); $subSkipped = $this->buildNativeSqlProcedure($subProcedure, $child['children']); - if (count($subSkipped) !== count($child['children'])) { + if (\count($subSkipped) !== \count($child['children'])) { $procedure->addSubProcedure($subProcedure); } @@ -324,9 +269,7 @@ protected function buildNativeSqlProcedure(FilterBuilderSql $procedure, $childre * Method to optimize as many system column lookup filters as possible into a combined filter rule. * * @param ConditionAnd|ConditionOr $filterRule The filter to which the optimized rule shall be added to. - * * @param array $children The children to parse. - * * @param string $operation The operation to parse (AND or OR). * * @return array @@ -336,8 +279,8 @@ protected function optimizedFilter($filterRule, $children, $operation) $procedure = new FilterBuilderSql($this->getMetaModel()->getTableName(), $operation, $this->connection, 't.'); $skipped = $this->buildNativeSqlProcedure($procedure, $children); - if (!$procedure->isEmpty()) { - $filterRule->addChild($this->getMetaModel()->getEmptyFilter()->addFilterRule($procedure->build())); + if (null !== ($rule = $procedure->build())) { + $filterRule->addChild($this->getMetaModel()->getEmptyFilter()->addFilterRule($rule)); } return $skipped; @@ -346,19 +289,18 @@ protected function optimizedFilter($filterRule, $children, $operation) /** * Build an AND or OR query. * - * @param IFilter $filter The filter to add the operations to. - * - * @param array $operation The operation to convert. + * @param IFilter $filter The filter to add the operations to. + * @param TFilterANDOR $operation The operation to convert. * * @return void */ protected function getAndOrFilter(IFilter $filter, $operation) { - if (!$operation['children']) { + if ([] === $operation['children']) { return; } - if ($operation['operation'] == 'AND') { + if ($operation['operation'] === 'AND') { $filterRule = new ConditionAnd(); } else { $filterRule = new ConditionOr(); @@ -377,7 +319,7 @@ protected function getAndOrFilter(IFilter $filter, $operation) /** * Retrieve the attribute for a filter operation. * - * @param array $operation The operation to retrieve the attribute for. + * @param TFilter $operation The operation to retrieve the attribute for. * * @return IAttribute * @@ -385,13 +327,13 @@ protected function getAndOrFilter(IFilter $filter, $operation) */ protected function getAttributeFromFilterOperation($operation) { - $attribute = null; - if (!empty($operation['property'])) { - $attribute = $this->getMetaModel()->getAttribute($operation['property']); + if (null === ($property = $operation['property'] ?? null)) { + throw new \InvalidArgumentException('Missing key "property" in ' . var_export($operation, true)); } + $attribute = $this->getMetaModel()->getAttribute($property); if ($attribute === null) { - throw new \InvalidArgumentException('Attribute ' . $operation['property'] . ' not found.'); + throw new \InvalidArgumentException('Attribute ' . $property . ' not found.'); } return $attribute; @@ -419,29 +361,26 @@ protected function getAttributeFromFilterOperation($operation) * 'property' string (the name of a property) * 'values' array of literal * - * @param array $operation The filter to be combined into the passed filter object. - * + * @param TFilter $operation The filter to be combined into the passed filter object. * @param IFilter $filter The filter object where the rules shall get appended to. * * @return void * * @throws \RuntimeException When an improper filter condition is encountered, an exception is thrown. */ - private function calculateSubfilter($operation, IFilter $filter) + private function calculateSubfilter(array $operation, IFilter $filter): void { - if (!is_array($operation)) { - throw new \RuntimeException('Error Processing subfilter: ' . var_export($operation, true), 1); - } - switch ($operation['operation']) { case 'AND': case 'OR': + $this->assertValidAndOr($operation); $this->getAndOrFilter($filter, $operation); break; case '=': case '>': case '<': + $this->assertValidCompareOperation($operation); $this->getFilterForComparingOperator( $this->getAttributeFromFilterOperation($operation), $filter, @@ -450,10 +389,12 @@ private function calculateSubfilter($operation, IFilter $filter) break; case 'IN': + $this->assertValidInList($operation); $this->getFilterForInList($filter, $operation); break; case 'LIKE': + $this->assertValidLike($operation); $this->getFilterForLike( $this->getAttributeFromFilterOperation($operation), $filter, @@ -463,7 +404,7 @@ private function calculateSubfilter($operation, IFilter $filter) default: throw new \RuntimeException( - 'Error processing filter array - unknown operation ' . var_export($operation, true), + 'Error processing filter array - unknown operation ' . \var_export($operation, true), 1 ); } @@ -478,17 +419,60 @@ public function build() { $filter = $this->getMetaModel()->getEmptyFilter(); - if ($this->configuration->getFilter()) { - $this->calculateSubfilter( - array - ( - 'operation' => 'AND', - 'children' => $this->configuration->getFilter() - ), - $filter - ); + /** @var null|list $filterRules */ + $filterRules = $this->configuration->getFilter(); + if (null !== $filterRules) { + $this->calculateSubfilter(['operation' => 'AND', 'children' => $filterRules], $filter); } return $filter; } + + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + private function buildSearchAttributeFilterRule(IAttribute $attribute, string $value): SearchAttribute + { + $languages = []; + $metaModel = $attribute->getMetaModel(); + if ($metaModel instanceof ITranslatedMetaModel) { + $languages = $metaModel->getLanguages(); + } elseif ($metaModel->isTranslated(false)) { + $languages = $this->getMetaModel()->getAvailableLanguages() ?? []; + } + + return new SearchAttribute($attribute, $value, \array_values(\array_filter($languages))); + } + + /** @psalm-assert TFilterANDOR $filter */ + private function assertValidAndOr(array $filter): void + { + assert(\is_array($filter['children'] ?? null)); + assert(\in_array($filter['operation'], ['AND', 'OR'], true)); + } + + /** @psalm-assert TFilterCMP $filter */ + private function assertValidCompareOperation(array $filter): void + { + assert(\is_string($filter['property'] ?? null)); + assert(\is_string($value = ($filter['value'] ?? null)) || \is_int($value) || \is_float($value)); + assert(\in_array($filter['operation'], ['<', '=', '>'], true)); + } + + /** @psalm-assert TFilterIN $filter */ + private function assertValidInList(array $filter): void + { + assert(\is_string($filter['property'] ?? null)); + assert(\is_string($value = ($filter['value'] ?? null)) || \is_int($value) || \is_float($value)); + assert($filter['operation'] === 'IN'); + } + + /** @psalm-assert TFilterLIKE $filter */ + private function assertValidLike(array $filter): void + { + assert(\is_string($filter['property'] ?? null)); + assert(\is_string($filter['value'] ?? null)); + assert($filter['operation'] === 'LIKE'); + } } diff --git a/src/DcGeneral/Data/FilterBuilderSql.php b/src/DcGeneral/Data/FilterBuilderSql.php index 8c9817b4f..05f67cfb5 100644 --- a/src/DcGeneral/Data/FilterBuilderSql.php +++ b/src/DcGeneral/Data/FilterBuilderSql.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Sven Baumann * @author David Molineus * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -44,51 +44,49 @@ class FilterBuilderSql * * @var string */ - private $combiner; + private string $combiner; /** * The SQL procedure. * - * @var string + * @var array */ - private $procedures = []; + private array $procedures = []; /** * The SQL query parameters. * * @var array */ - private $parameter = []; + private array $parameter = []; /** * The database instance. * * @var Connection */ - private $connection; + private Connection $connection; /** * The table alias to use. * * @var string */ - private $tableAlias; + private string $tableAlias; /** * Create a new instance. * * @param string $tableName The table name. - * * @param string $combiner The combiner (AND or OR). - * * @param Connection $connection The database connection. - * * @param string $tableAlias The table alias prefix (defaults to 't.'). */ public function __construct($tableName, $combiner, $connection, string $tableAlias = 't.') { $this->tableName = $tableName; $this->combiner = strtoupper($combiner); + /** @psalm-suppress DeprecatedMethod */ $this->connection = $this->sanitizeConnection($connection); $this->tableAlias = $tableAlias; } @@ -110,7 +108,7 @@ public function isEmpty() */ public function getProcedure() { - return '(' . implode(' ' . $this->combiner . ' ', $this->procedures) . ')'; + return '(' . \implode(' ' . $this->combiner . ' ', $this->procedures) . ')'; } /** @@ -132,7 +130,7 @@ public function build() { if (!$this->isEmpty()) { return new SimpleQuery( - sprintf('SELECT t.id FROM %s AS t WHERE %s', $this->tableName, $this->getProcedure()), + \sprintf('SELECT t.id FROM %s AS t WHERE %s', $this->tableName, $this->getProcedure()), $this->getParameters(), 'id', $this->connection @@ -152,7 +150,7 @@ public function build() protected function getFilterForComparingOperator($operation) { $this->parameter[] = $operation['value']; - $this->procedures[] = sprintf( + $this->procedures[] = \sprintf( '(%s%s %s ?)', $this->tableAlias, $operation['property'], @@ -171,12 +169,12 @@ protected function getFilterForComparingOperator($operation) */ protected function getFilterForInList($operation) { - $this->parameter = array_merge($this->parameter, array_values($operation['values'])); - $this->procedures[] = sprintf( + $this->parameter = \array_merge($this->parameter, \array_values($operation['values'])); + $this->procedures[] = \sprintf( '(%s%s IN (%s))', $this->tableAlias, $operation['property'], - rtrim(str_repeat('?,', \count($operation['values'])), ',') + \rtrim(\str_repeat('?,', \count($operation['values'])), ',') ); return $this; @@ -193,8 +191,8 @@ protected function getFilterForInList($operation) */ protected function getFilterForLike($operation) { - $this->parameter[] = str_replace(array('*', '?'), array('%', '_'), $operation['value']); - $this->procedures[] = sprintf('(%s%s LIKE ?)', $this->tableAlias, $operation['property']); + $this->parameter[] = \str_replace(array('*', '?'), array('%', '_'), $operation['value']); + $this->procedures[] = \sprintf('(%s%s LIKE ?)', $this->tableAlias, $operation['property']); return $this; } @@ -210,11 +208,7 @@ protected function getFilterForLike($operation) */ public function addChild($child) { - if (!\is_array($child)) { - throw new \RuntimeException('Error Processing sub filter: ' . var_export($child, true), 1); - } - - switch (strtoupper($child['operation'])) { + switch (\strtoupper($child['operation'])) { case '=': case '>': case '<': @@ -229,7 +223,7 @@ public function addChild($child) default: } - throw new \RuntimeException('Error processing filter array ' . var_export($child, true), 1); + throw new \RuntimeException('Error processing filter array ' . \var_export($child, true), 1); } /** @@ -242,7 +236,7 @@ public function addChild($child) public function addSubProcedure(FilterBuilderSql $subProcedure) { $this->procedures[] = $subProcedure->getProcedure(); - $this->parameter = array_merge($this->parameter, $subProcedure->getParameters()); + $this->parameter = \array_merge($this->parameter, $subProcedure->getParameters()); return $this; } @@ -250,7 +244,7 @@ public function addSubProcedure(FilterBuilderSql $subProcedure) /** * Sanitize the connection value * - * @param Connection|\Contao\Database $connection The connection value. + * @param \Contao\Database|Connection|null $connection The connection value. * * @return Connection * @@ -258,7 +252,7 @@ public function addSubProcedure(FilterBuilderSql $subProcedure) * * @deprecated To be removed in 3.0 - you should ALWAYS pass the proper connection. */ - private function sanitizeConnection($connection) + private function sanitizeConnection(Connection|Database|null $connection): Connection { if ($connection instanceof Connection) { return $connection; @@ -275,8 +269,10 @@ private function sanitizeConnection($connection) ); $reflection = new \ReflectionProperty(Database::class, 'resConnection'); $reflection->setAccessible(true); + return $reflection->getValue($connection); } + if (null === $connection) { @trigger_error( 'You should pass a doctrine database connection to "' . __METHOD__ . '".', diff --git a/src/DcGeneral/Data/Model.php b/src/DcGeneral/Data/Model.php index 87c96eb7c..1b267cd30 100644 --- a/src/DcGeneral/Data/Model.php +++ b/src/DcGeneral/Data/Model.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Stefan Heimes * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +26,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBagInterface; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidArgumentException; use ContaoCommunityAlliance\DcGeneral\Exception\DcGeneralInvalidPropertyValueException; +use MetaModels\Attribute\IAttribute; use MetaModels\Exceptions\DifferentValuesException; use MetaModels\IItem; use MetaModels\ITranslatedMetaModel; @@ -34,11 +36,10 @@ */ class Model implements ModelInterface { - /** * The MetaModel item accessible via this instance. * - * @var IItem + * @var IItem|null */ protected $objItem = null; @@ -47,7 +48,7 @@ class Model implements ModelInterface * * @var array */ - protected $arrMetaInformation = array(); + protected $arrMetaInformation = []; /** * The language of the contained data. @@ -63,20 +64,23 @@ class Model implements ModelInterface */ protected function getPropertyNames() { - $propertyNames = array('id', 'pid', 'tstamp', 'sorting'); + $propertyNames = ['id', 'pid', 'tstamp', 'sorting']; + + $item = $this->getItem(); + assert($item instanceof IItem); - if ($this->getItem()->getMetaModel()->hasVariants()) { + if ($item->getMetaModel()->hasVariants()) { $propertyNames[] = 'varbase'; $propertyNames[] = 'vargroup'; } - return array_merge($propertyNames, array_keys($this->getItem()->getMetaModel()->getAttributes())); + return \array_merge($propertyNames, \array_keys($item->getMetaModel()->getAttributes())); } /** * Returns the native IMetaModelItem instance encapsulated within this abstraction. * - * @return IItem + * @return IItem|null */ public function getItem() { @@ -99,7 +103,9 @@ public function __construct($objItem, ?string $language = null) */ public function __clone() { - $this->objItem = $this->getItem()->copy(); + $item = $this->getItem(); + assert($item instanceof IItem); + $this->objItem = $item->copy(); } /** @@ -107,24 +113,28 @@ public function __clone() */ public function getId() { - return $this->getItem()->get('id'); + $item = $this->getItem(); + assert($item instanceof IItem); + + return $item->get('id'); } /** * {@inheritDoc} */ - public function getProperty($strPropertyName) + public function getProperty($propertyName) { - if ($this->getItem()) { - $varValue = $this->getItem()->get($strPropertyName); + if (null !== ($item = $this->getItem())) { + $varValue = $item->get($propertyName); // Test if it is an attribute, if so, let it transform the data for the widget. - $objAttribute = $this->getItem()->getAttribute($strPropertyName); + $objAttribute = $item->getAttribute($propertyName); if ($objAttribute) { $varValue = $objAttribute->valueToWidget($varValue); } return $varValue; } + return null; } @@ -133,11 +143,12 @@ public function getProperty($strPropertyName) */ public function getPropertiesAsArray() { - $arrResult = array(); + $arrResult = []; foreach ($this->getPropertyNames() as $strKey) { $arrResult[$strKey] = $this->getProperty($strKey); } + return $arrResult; } @@ -146,19 +157,22 @@ public function getPropertiesAsArray() */ public function getMeta($strMetaName) { - if (array_key_exists($strMetaName, $this->arrMetaInformation)) { + if (\array_key_exists($strMetaName, $this->arrMetaInformation)) { return $this->arrMetaInformation[$strMetaName]; } + return null; } /** * {@inheritDoc} */ - public function setId($mixID) + public function setId($mixId) { - if ($this->getId() == null) { - $this->getItem()->set('id', $mixID); + if ($this->getId() === null) { + $item = $this->getItem(); + assert($item instanceof IItem); + $item->set('id', $mixId); $this->setMeta(static::IS_CHANGED, true); } } @@ -173,38 +187,40 @@ public function setId($mixID) */ public function setProperty($strPropertyName, $varValue) { - if ($this->getItem()) { + if (null !== ($item = $this->getItem())) { $varInternalValue = $varValue; // Test if it is an attribute, if so, let it transform the data for the widget. - $objAttribute = $this->getItem()->getAttribute($strPropertyName); + $objAttribute = $item->getAttribute($strPropertyName); - if ($objAttribute) { + if (null !== $objAttribute) { $model = $objAttribute->getMetaModel(); $originalLanguage = null; + /** @psalm-suppress DeprecatedMethod */ if (null !== $this->language) { if ($model instanceof ITranslatedMetaModel) { $originalLanguage = $model->selectLanguage($this->language); + /** @psalm-suppress DeprecatedMethod */ } elseif ($model->isTranslated()) { $originalLanguage = $GLOBALS['TL_LANGUAGE']; $GLOBALS['TL_LANGUAGE'] = $this->language; } } - $varInternalValue = $objAttribute->widgetToValue($varValue, $this->getItem()->get('id')); + $varInternalValue = $objAttribute->widgetToValue($varValue, $item->get('id')); } try { if ($varValue !== $this->getProperty($strPropertyName)) { $this->setMeta(static::IS_CHANGED, true); - $this->getItem()->set($strPropertyName, $varInternalValue); + $item->set($strPropertyName, $varInternalValue); try { DifferentValuesException::compare($varValue, $this->getProperty($strPropertyName), false); } catch (DifferentValuesException $exception) { throw new DcGeneralInvalidPropertyValueException( - sprintf( + \sprintf( 'Property %s (%s) did not accept the value (%s).', $strPropertyName, - $objAttribute->get('type'), + $objAttribute ? ((string) $objAttribute->get('type')) : '?', $exception->getLongMessage() ), 1, @@ -227,9 +243,9 @@ public function setProperty($strPropertyName, $varValue) /** * {@inheritDoc} */ - public function setPropertiesAsArray($arrProperties) + public function setPropertiesAsArray($properties) { - foreach ($arrProperties as $strKey => $varValue) { + foreach ($properties as $strKey => $varValue) { $this->setProperty($strKey, $varValue); } } @@ -247,12 +263,15 @@ public function setMeta($strMetaName, $varValue) */ public function hasProperties() { - return ($this->getItem()) ? true : false; + return (bool) $this->getItem(); } /** * {@inheritDoc} + * + * @return \Traversable */ + #[\ReturnTypeWillChange] public function getIterator() { return new ModelIterator($this); @@ -263,7 +282,10 @@ public function getIterator() */ public function getProviderName() { - return $this->getItem()->getMetaModel()->getTableName(); + $item = $this->getItem(); + assert($item instanceof IItem); + + return $item->getMetaModel()->getTableName(); } /** @@ -284,6 +306,8 @@ public function readFromPropertyValueBag(PropertyValueBagInterface $valueBag) $this->setProperty($property, $valueBag->getPropertyValue($property)); } + + return $this; } /** @@ -298,5 +322,7 @@ public function writeToPropertyValueBag(PropertyValueBagInterface $valueBag) $valueBag->setPropertyValue($property, $this->getProperty($property)); } + + return $this; } } diff --git a/src/DcGeneral/Data/ModelIterator.php b/src/DcGeneral/Data/ModelIterator.php index 7d4b0b86f..dcb10bf1e 100644 --- a/src/DcGeneral/Data/ModelIterator.php +++ b/src/DcGeneral/Data/ModelIterator.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,23 +13,28 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\DcGeneral\Data; +use MetaModels\IItem; + /** * Iterator class for allowing usage of MetaModels\DcGeneral\Data\Model * in foreach constructs. + * + * @implements \Iterator */ class ModelIterator implements \Iterator { /** * The model to iterate over. * - * @var Model + * @var Model|null */ protected $objModel = null; @@ -38,14 +43,14 @@ class ModelIterator implements \Iterator * * @var int */ - private $intPosition = 0; + private int $intPosition = 0; /** * All property names. * * @var string[] */ - protected $arrKeys = array(); + protected $arrKeys = []; /** * Returns an array containing all property names. @@ -66,14 +71,16 @@ public function __construct(Model $objModel) { $this->intPosition = 0; $this->objModel = $objModel; - $objMetaModel = $this->objModel->getItem()->getMetaModel(); + $item = $this->objModel->getItem(); + assert($item instanceof IItem); + $objMetaModel = $item->getMetaModel(); - $arrKeys = array(); + $arrKeys = []; if ($objMetaModel->hasVariants()) { $arrKeys[] = 'varbase'; $arrKeys[] = 'vargroup'; } - $this->arrKeys = array_merge($arrKeys, array_keys($objMetaModel->getAttributes())); + $this->arrKeys = \array_merge($arrKeys, \array_keys($objMetaModel->getAttributes())); } /** @@ -81,7 +88,7 @@ public function __construct(Model $objModel) * * @return void */ - public function rewind() + public function rewind(): void { $this->intPosition = 0; } @@ -91,9 +98,12 @@ public function rewind() * * @return mixed */ - public function current() + public function current(): mixed { - return $this->objModel->getProperty($this->key()); + $model = $this->objModel; + assert($model instanceof Model); + + return $model->getProperty($this->key()); } /** @@ -101,9 +111,10 @@ public function current() * * @return string */ - public function key() + public function key(): string { $arrKeys = $this->getKeys(); + return $arrKeys[$this->intPosition]; } @@ -112,7 +123,7 @@ public function key() * * @return void */ - public function next() + public function next(): void { ++$this->intPosition; } @@ -122,8 +133,8 @@ public function next() * * @return bool */ - public function valid() + public function valid(): bool { - return strlen($this->key()) > 0; + return \strlen($this->key()) > 0; } } diff --git a/src/DcGeneral/DataDefinition/Definition/IMetaModelDefinition.php b/src/DcGeneral/DataDefinition/Definition/IMetaModelDefinition.php index f28d19ac7..2cd7d27b5 100644 --- a/src/DcGeneral/DataDefinition/Definition/IMetaModelDefinition.php +++ b/src/DcGeneral/DataDefinition/Definition/IMetaModelDefinition.php @@ -30,7 +30,7 @@ interface IMetaModelDefinition extends DefinitionInterface /** * The name of the definition. */ - const NAME = 'metamodels'; + public const NAME = 'metamodels'; /** * Set the id of the active render setting for the MetaModel. diff --git a/src/DcGeneral/DataDefinition/Definition/MetaModelDefinition.php b/src/DcGeneral/DataDefinition/Definition/MetaModelDefinition.php index a1506f765..78f3f29ee 100644 --- a/src/DcGeneral/DataDefinition/Definition/MetaModelDefinition.php +++ b/src/DcGeneral/DataDefinition/Definition/MetaModelDefinition.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -22,6 +23,8 @@ /** * Default implementation of IMetaModelDefinition. + * + * @psalm-suppress MissingConstructor */ class MetaModelDefinition implements IMetaModelDefinition { diff --git a/src/DcGeneral/DataDefinition/MetaModelDataDefinition.php b/src/DcGeneral/DataDefinition/MetaModelDataDefinition.php index f1330f716..dd23bdb13 100644 --- a/src/DcGeneral/DataDefinition/MetaModelDataDefinition.php +++ b/src/DcGeneral/DataDefinition/MetaModelDataDefinition.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -51,6 +52,9 @@ public function hasMetaModelDefinition() */ public function getMetaModelDefinition() { - return $this->getDefinition(IMetaModelDefinition::NAME); + $definition = $this->getDefinition(IMetaModelDefinition::NAME); + assert($definition instanceof IMetaModelDefinition); + + return $definition; } } diff --git a/src/DcGeneral/DataDefinition/Palette/Condition/Palette/RenderSettingAttributeIs.php b/src/DcGeneral/DataDefinition/Palette/Condition/Palette/RenderSettingAttributeIs.php index 7aa9ede9b..9d36ed815 100644 --- a/src/DcGeneral/DataDefinition/Palette/Condition/Palette/RenderSettingAttributeIs.php +++ b/src/DcGeneral/DataDefinition/Palette/Condition/Palette/RenderSettingAttributeIs.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author David Molineus * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -29,6 +29,7 @@ use ContaoCommunityAlliance\DcGeneral\Data\PropertyValueBag; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Palette\AbstractWeightAwarePaletteCondition; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\IMetaModelsServiceContainer; use MetaModels\MetaModelsServiceContainer; @@ -49,20 +50,19 @@ class RenderSettingAttributeIs extends AbstractWeightAwarePaletteCondition * * @var array */ - protected static $attributeTypes = array(); + protected static $attributeTypes = []; /** * Database connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * Create a new instance. * * @param string $attributeType The attribute type name. - * * @param int $weight The weight of this condition to apply. * * @param Connection|null $connection Database connection. @@ -80,8 +80,8 @@ public function __construct($attributeType, $weight = 1, Connection $connection ); // @codingStandardsIgnoreEnd $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); } - $this->connection = $connection; } @@ -116,10 +116,11 @@ public function getAttributeType() * * @return string * - * @throws \Doctrine\DBAL\DBALException When an database error occurs. + * @throws Exception When an database error occurs. */ public function getTypeOfAttribute($value) { + if (!isset(self::$attributeTypes[$value])) { $statement = $this->connection ->createQueryBuilder() @@ -128,9 +129,13 @@ public function getTypeOfAttribute($value) ->where('t.id=:id') ->setParameter('id', $value) ->setMaxResults(1) - ->execute(); + ->executeQuery(); - self::$attributeTypes[$value] = $statement->fetch(\PDO::FETCH_OBJ)->type; + if (false === ($result = $statement->fetchAssociative())) { + return new Exception('Failed to load attribute for render setting.'); + } + + self::$attributeTypes[$value] = $result['type']; } return self::$attributeTypes[$value]; @@ -141,6 +146,7 @@ public function getTypeOfAttribute($value) */ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $input = null) { + $value = null; if ($input && $input->hasPropertyValue('attr_id')) { $value = $input->getPropertyValue('attr_id'); } elseif ($model) { @@ -149,6 +155,10 @@ public function getMatchCount(ModelInterface $model = null, PropertyValueBag $in return false; } + if (null === $value) { + return false; + } + return ($this->getTypeOfAttribute($value) == $this->getAttributeType()) ? $this->getWeight() : false; } @@ -165,9 +175,15 @@ public function __clone() * @return IMetaModelsServiceContainer * * @deprecated + * + * @psalm-suppress DeprecatedInterface + * @psalm-suppress DeprecatedClass */ protected function getServiceContainer(): IMetaModelsServiceContainer { - return System::getContainer()->get(MetaModelsServiceContainer::class); + $serviceContainer = System::getContainer()->get(MetaModelsServiceContainer::class); + assert($serviceContainer instanceof IMetaModelsServiceContainer); + + return $serviceContainer; } } diff --git a/src/DcGeneral/DataDefinition/Palette/Condition/Property/AttributeByIdIsOfType.php b/src/DcGeneral/DataDefinition/Palette/Condition/Property/AttributeByIdIsOfType.php index 5c184a919..5c2828bce 100644 --- a/src/DcGeneral/DataDefinition/Palette/Condition/Property/AttributeByIdIsOfType.php +++ b/src/DcGeneral/DataDefinition/Palette/Condition/Property/AttributeByIdIsOfType.php @@ -103,7 +103,8 @@ public function getAttributeType() * * @param int $value The id of an attribute. * - * @return string + * @return ?string Return the id or null if not found. + * @throws \Doctrine\DBAL\Exception */ public function getTypeOfAttribute($value) { @@ -116,9 +117,14 @@ public function getTypeOfAttribute($value) ->where('t.id=:id') ->setParameter('id', $value) ->setMaxResults(1) - ->execute(); - - self::$attributeTypes[$value] = $statement->fetch(\PDO::FETCH_COLUMN); + ->executeQuery(); + + $result = $statement->fetchFirstColumn(); + if (count($result) > 0) { + self::$attributeTypes[$value] = \current($result); + } else { + self::$attributeTypes[$value] = null; + } } return self::$attributeTypes[$value]; diff --git a/src/DcGeneral/DataDefinition/Palette/Condition/Property/ConditionTableNameIsMetaModel.php b/src/DcGeneral/DataDefinition/Palette/Condition/Property/ConditionTableNameIsMetaModel.php index 0f89d8fdb..c98c8ef48 100644 --- a/src/DcGeneral/DataDefinition/Palette/Condition/Property/ConditionTableNameIsMetaModel.php +++ b/src/DcGeneral/DataDefinition/Palette/Condition/Property/ConditionTableNameIsMetaModel.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,7 +33,7 @@ class ConditionTableNameIsMetaModel implements PropertyConditionInterface { /** - * The name of the property in the passed model which contains the table name. + * The name of the property in the past model which contains the table name. * * @var string */ @@ -48,9 +49,8 @@ class ConditionTableNameIsMetaModel implements PropertyConditionInterface /** * Create a new instance. * - * @param string $tableProperty The name of the property in the passed model which contains the table name. - * - * @param string $desiredValue The desired value, true if the table shall be a MetaModel, false otherwise. + * @param string $tableProperty The name of the property in the past model which contains the table name. + * @param bool $desiredValue The desired value, true if the table shall be a MetaModel, false otherwise. */ public function __construct($tableProperty, $desiredValue) { @@ -97,7 +97,7 @@ public function setTablePropertyName($tablePropertyName) } /** - * Retrieve the name of the property in the passed model which contains the table name. + * Retrieve the name of the property in the past model which contains the table name. * * @return string */ @@ -124,7 +124,7 @@ public function match( return false; } - return $this->desiredValue == (substr($value, 0, 3) === 'mm_'); + return $this->desiredValue == (\str_starts_with($value, 'mm_')); } /** diff --git a/src/DcGeneral/DataDefinition/Palette/Condition/Property/FilterSettingTypeSubPaletteCondition.php b/src/DcGeneral/DataDefinition/Palette/Condition/Property/FilterSettingTypeSubPaletteCondition.php index 8289a4211..73fad186b 100644 --- a/src/DcGeneral/DataDefinition/Palette/Condition/Property/FilterSettingTypeSubPaletteCondition.php +++ b/src/DcGeneral/DataDefinition/Palette/Condition/Property/FilterSettingTypeSubPaletteCondition.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2022 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package MetaModels/core * @author David Molineus - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2022 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -72,7 +72,7 @@ public function match( } $metaModel = $this->getMetaModel($model); - $attribute = $metaModel->getAttributeById($model->getProperty('attr_id')); + $attribute = $metaModel->getAttributeById((int) $model->getProperty('attr_id')); if (!$attribute) { return false; diff --git a/src/DcGeneral/DataDefinition/Palette/Condition/Property/InputScreenAttributeIs.php b/src/DcGeneral/DataDefinition/Palette/Condition/Property/InputScreenAttributeIs.php index 82f0c7e22..f5c18fab7 100644 --- a/src/DcGeneral/DataDefinition/Palette/Condition/Property/InputScreenAttributeIs.php +++ b/src/DcGeneral/DataDefinition/Palette/Condition/Property/InputScreenAttributeIs.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author Cliff Parnitzky * @author David Molineus * @author Richard Henkenjohann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -58,6 +59,7 @@ public function __construct($attributeType, Connection $connection = null) ); // @codingStandardsIgnoreEnd $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); } parent::__construct($attributeType, $connection, 'attr_id'); @@ -69,9 +71,15 @@ public function __construct($attributeType, Connection $connection = null) * @return IMetaModelsServiceContainer * * @deprecated + * + * @psalm-suppress DeprecatedInterface */ protected function getServiceContainer(): IMetaModelsServiceContainer { - return System::getContainer()->get(MetaModelsServiceContainer::class); + /** @psalm-suppress DeprecatedClass */ + $serviceContainer = System::getContainer()->get(MetaModelsServiceContainer::class); + assert($serviceContainer instanceof IMetaModelsServiceContainer); + + return $serviceContainer; } } diff --git a/src/DcGeneral/DataDefinition/Palette/Condition/Property/InputScreenRenderModeIs.php b/src/DcGeneral/DataDefinition/Palette/Condition/Property/InputScreenRenderModeIs.php index 660416013..ca107c733 100644 --- a/src/DcGeneral/DataDefinition/Palette/Condition/Property/InputScreenRenderModeIs.php +++ b/src/DcGeneral/DataDefinition/Palette/Condition/Property/InputScreenRenderModeIs.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author David Molineus * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,12 +30,15 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\LegendInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; -use Doctrine\DBAL\Driver\Connection; +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\IMetaModelsServiceContainer; use MetaModels\MetaModelsServiceContainer; /** * Condition for the default palette. + * + * @psalm-suppress PropertyNotSetInConstructor */ class InputScreenRenderModeIs implements PropertyConditionInterface { @@ -58,13 +61,12 @@ class InputScreenRenderModeIs implements PropertyConditionInterface * * @var Connection */ - private $connection; + private Connection $connection; /** * Create a new instance. * * @param string $desiredState The desired state. - * * @param Connection|null $connection Database connection. */ public function __construct($desiredState, Connection $connection = null) @@ -79,8 +81,8 @@ public function __construct($desiredState, Connection $connection = null) ); // @codingStandardsIgnoreEnd $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); } - $this->connection = $connection; } @@ -114,20 +116,26 @@ public function getRenderMode() * @param int $value The id of an input screen. * * @return string + * + * @throws Exception */ public function getInputScreenRenderMode($value) { if (!isset(self::$stateBuffer[$value])) { - $statement =$this->connection + $statement = $this->connection ->createQueryBuilder() ->select('t.rendermode') ->from('tl_metamodel_dca', 't') ->where('t.id=:id') ->setParameter('id', $value) ->setMaxResults(1) - ->execute(); + ->executeQuery(); - self::$stateBuffer[$value] = $statement->fetch(\PDO::FETCH_OBJ)->rendermode; + if (false === ($result = $statement->fetchAssociative())) { + return new Exception('Failed to load attribute for input screen setting.'); + } + + self::$stateBuffer[$value] = $result['rendermode']; } return self::$stateBuffer[$value]; @@ -166,9 +174,15 @@ public function __clone() * @return IMetaModelsServiceContainer * * @deprecated + * + * @psalm-suppress DeprecatedInterface */ protected function getServiceContainer(): IMetaModelsServiceContainer { - return System::getContainer()->get(MetaModelsServiceContainer::class); + /** @psalm-suppress DeprecatedClass */ + $serviceContainer = System::getContainer()->get(MetaModelsServiceContainer::class); + assert($serviceContainer instanceof IMetaModelsServiceContainer); + + return $serviceContainer; } } diff --git a/src/DcGeneral/DataDefinition/Palette/Condition/Property/IsVariantAttribute.php b/src/DcGeneral/DataDefinition/Palette/Condition/Property/IsVariantAttribute.php index 91894aaed..dabcfa60f 100644 --- a/src/DcGeneral/DataDefinition/Palette/Condition/Property/IsVariantAttribute.php +++ b/src/DcGeneral/DataDefinition/Palette/Condition/Property/IsVariantAttribute.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Stefan Heimes * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,9 +27,11 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\LegendInterface; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\PropertyInterface; +use MetaModels\DcGeneral\Data\Model; +use MetaModels\IItem; /** - * This condition matches as soon as all of the following apply: + * This condition matches as soon as all the following apply: * 1. the MetaModel supports variants. * 2. the current item is not a variant base. * 3. the attribute is not an invariant attribute. @@ -45,17 +47,15 @@ public function match( PropertyInterface $property = null, LegendInterface $legend = null ) { - if ($property === null || $model === null) { + if ($property === null || !($model instanceof Model)) { return false; } - - /** @var $model \MetaModels\DcGeneral\Data\Model */ - $nativeItem = $model->getItem(); - $metaModel = $nativeItem->getMetaModel(); + assert($nativeItem instanceof IItem); + $metaModel = $nativeItem->getMetaModel(); if ($metaModel->hasVariants() && !$nativeItem->isVariantBase()) { - return !in_array($property->getName(), array_keys($metaModel->getInVariantAttributes())); + return !\in_array($property->getName(), \array_keys($metaModel->getInVariantAttributes())); } return true; diff --git a/src/DcGeneral/DataDefinition/Palette/Condition/Property/PropertyContainAnyOfCondition.php b/src/DcGeneral/DataDefinition/Palette/Condition/Property/PropertyContainAnyOfCondition.php index b8e8b36ff..10c21b6b9 100644 --- a/src/DcGeneral/DataDefinition/Palette/Condition/Property/PropertyContainAnyOfCondition.php +++ b/src/DcGeneral/DataDefinition/Palette/Condition/Property/PropertyContainAnyOfCondition.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author Sven Baumann * @author Stefan Heimes * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -75,9 +75,9 @@ class PropertyContainAnyOfCondition implements PropertyConditionInterface */ public function __construct($propertyName = '', $propertyValue = null, $strict = false) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; $this->propertyValue = $propertyValue; - $this->strict = (bool) $strict; + $this->strict = $strict; $this->metaModel = null; } @@ -110,7 +110,7 @@ public function setMetaModel(IMetaModel $metaModel): void */ public function setPropertyName($propertyName) { - $this->propertyName = (string) $propertyName; + $this->propertyName = $propertyName; return $this; } @@ -156,7 +156,7 @@ public function getPropertyValue() */ public function setStrict($strict) { - $this->strict = (bool) $strict; + $this->strict = $strict; return $this; } @@ -184,12 +184,15 @@ public function match( PropertyInterface $property = null, LegendInterface $legend = null ) { - $attribute = $this->metaModel->getAttribute($this->propertyName); - + assert($this->metaModel instanceof IMetaModel); if ($this->metaModel instanceof ITranslatedMetaModel) { $currentLanguage = $this->metaModel->getLanguage(); } else { - $currentLanguage = $this->metaModel->getActiveLanguage(); + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + $currentLanguage = $this->metaModel->isTranslated(false) ? $this->metaModel->getActiveLanguage() : ''; } if ($input && $input->hasPropertyValue($this->propertyName)) { @@ -211,6 +214,7 @@ public function match( return false; } + $attribute = $this->metaModel->getAttribute($this->propertyName); foreach ($values as $value) { if ($value && $attribute instanceof IAliasConverter) { $value = ($attribute->getIdForAlias($value, $currentLanguage) ?? $value); diff --git a/src/DcGeneral/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php b/src/DcGeneral/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php index 529e4143c..06538caa8 100644 --- a/src/DcGeneral/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php +++ b/src/DcGeneral/DataDefinition/Palette/Condition/Property/PropertyValueCondition.php @@ -140,6 +140,7 @@ public function match( if ($this->metaModel instanceof ITranslatedMetaModel) { $currentLanguage = $this->metaModel->getLanguage(); } else { + /** @psalm-suppress DeprecatedMethod */ $currentLanguage = $this->metaModel->getActiveLanguage(); } diff --git a/src/DcGeneral/DataDefinition/Palette/Condition/Property/RenderSettingAttributeIs.php b/src/DcGeneral/DataDefinition/Palette/Condition/Property/RenderSettingAttributeIs.php index 5e267c54d..520f2b194 100644 --- a/src/DcGeneral/DataDefinition/Palette/Condition/Property/RenderSettingAttributeIs.php +++ b/src/DcGeneral/DataDefinition/Palette/Condition/Property/RenderSettingAttributeIs.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,8 @@ * @author Cliff Parnitzky * @author David Molineus * @author Richard Henkenjohann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -58,6 +59,7 @@ public function __construct($attributeType, Connection $connection = null) ); // @codingStandardsIgnoreEnd $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); } parent::__construct($attributeType, $connection, 'attr_id'); @@ -69,9 +71,15 @@ public function __construct($attributeType, Connection $connection = null) * @return IMetaModelsServiceContainer * * @deprecated + * + * @psalm-suppress DeprecatedInterface */ protected function getServiceContainer(): IMetaModelsServiceContainer { - return System::getContainer()->get(MetaModelsServiceContainer::class); + /** @psalm-suppress DeprecatedClass */ + $serviceContainer = System::getContainer()->get(MetaModelsServiceContainer::class); + assert($serviceContainer instanceof IMetaModelsServiceContainer); + + return $serviceContainer; } } diff --git a/src/DcGeneral/Events/BaseSubscriber.php b/src/DcGeneral/Events/BaseSubscriber.php index 2799efc96..86e313fa2 100644 --- a/src/DcGeneral/Events/BaseSubscriber.php +++ b/src/DcGeneral/Events/BaseSubscriber.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -34,6 +35,8 @@ class BaseSubscriber * The MetaModel service container. * * @var IMetaModelsServiceContainer + * + * @psalm-suppress DeprecatedInterface */ protected $serviceContainer; @@ -41,6 +44,8 @@ class BaseSubscriber * Create a new instance. * * @param IMetaModelsServiceContainer $serviceContainer The MetaModel service container. + * + * @psalm-suppress DeprecatedInterface */ public function __construct(IMetaModelsServiceContainer $serviceContainer) { @@ -53,6 +58,8 @@ public function __construct(IMetaModelsServiceContainer $serviceContainer) * Retrieve the service container. * * @return IMetaModelsServiceContainer + * + * @psalm-suppress DeprecatedInterface */ protected function getServiceContainer() { @@ -66,6 +73,7 @@ protected function getServiceContainer() */ protected function getDatabase() { + /** @psalm-suppress DeprecatedMethod */ return $this->getServiceContainer()->getDatabase(); } @@ -83,15 +91,14 @@ protected function registerEventsInDispatcher() * Register multiple event listeners. * * @param string $eventName The event name to register. - * * @param callable $listener The listener to register. - * * @param int $priority The priority. * * @return BaseSubscriber */ public function addListener($eventName, $listener, $priority = 200) { + /** @psalm-suppress DeprecatedMethod */ $dispatcher = $this->getServiceContainer()->getEventDispatcher(); $dispatcher->addListener($eventName, $listener, $priority); @@ -101,13 +108,14 @@ public function addListener($eventName, $listener, $priority = 200) /** * Retrieve the MetaModel with the given id. * - * @param int $modelId The model being processed. + * @param string $modelId The model being processed. * - * @return IMetaModel + * @return IMetaModel|null */ protected function getMetaModelById($modelId) { - $services = $this->getServiceContainer(); + $services = $this->getServiceContainer(); + /** @psalm-suppress DeprecatedMethod */ $modelFactory = $services->getFactory(); $name = $modelFactory->translateIdToMetaModelName($modelId); diff --git a/src/DcGeneral/Events/MetaModel/BuildAttributeEvent.php b/src/DcGeneral/Events/MetaModel/BuildAttributeEvent.php index 541587e96..96e2a7392 100644 --- a/src/DcGeneral/Events/MetaModel/BuildAttributeEvent.php +++ b/src/DcGeneral/Events/MetaModel/BuildAttributeEvent.php @@ -33,7 +33,7 @@ class BuildAttributeEvent extends AbstractContainerAwareEvent /** * The event name. */ - const NAME = 'metamodels.dc-general.events.metamodel.build.attribute'; + public const NAME = 'metamodels.dc-general.events.metamodel.build.attribute'; /** * The Attribute. diff --git a/src/DcGeneral/Events/MetaModel/BuildMetaModelOperationsEvent.php b/src/DcGeneral/Events/MetaModel/BuildMetaModelOperationsEvent.php index 6757c104d..dc37767e7 100644 --- a/src/DcGeneral/Events/MetaModel/BuildMetaModelOperationsEvent.php +++ b/src/DcGeneral/Events/MetaModel/BuildMetaModelOperationsEvent.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,18 +13,21 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\DcGeneral\Events\MetaModel; +use Contao\System; use ContaoCommunityAlliance\DcGeneral\DataDefinition\ContainerInterface; use ContaoCommunityAlliance\DcGeneral\Event\AbstractContainerAwareEvent; use MetaModels\BackendIntegration\InputScreen\IInputScreen; use MetaModels\BackendIntegration\InputScreen\InputScreen; use MetaModels\IMetaModel; +use MetaModels\IMetaModelsServiceContainer; /** * This event is triggered to allow adding of model operations for MetaModels when the data container is being built. @@ -34,7 +37,7 @@ class BuildMetaModelOperationsEvent extends AbstractContainerAwareEvent /** * The event name. */ - const NAME = 'metamodels.dc-general.events.metamodel.build.metamodel.operations'; + public const NAME = 'metamodels.dc-general.events.metamodel.build.metamodel.operations'; /** * The MetaModel instance. @@ -54,9 +57,7 @@ class BuildMetaModelOperationsEvent extends AbstractContainerAwareEvent * Create a new container aware event. * * @param IMetaModel $metaModel The MetaModel. - * * @param ContainerInterface $dataContainer The data container information. - * * @param array $inputScreen The input screen in use. */ public function __construct( @@ -96,11 +97,17 @@ public function getScreen() * @return IInputScreen * * @deprecated The InputScreen class will get removed. + * + * @psalm-suppress DeprecatedInterface + * @psalm-suppress DeprecatedClass */ public function getInputScreen() { + $serviceContainer = System::getContainer()->get('cca.legacy_dic')?->getService('metamodels-service-container'); + assert($serviceContainer instanceof IMetaModelsServiceContainer); + return new InputScreen( - \System::getContainer()->get('cca.legacy_dic')->getService('metamodels-service-container'), + $serviceContainer, $this->inputScreen['meta'], $this->inputScreen['properties'], $this->inputScreen['conditions'], diff --git a/src/DcGeneral/Events/MetaModel/DuplicateModel.php b/src/DcGeneral/Events/MetaModel/DuplicateModel.php index 60bb8cba7..cbf69dbd3 100644 --- a/src/DcGeneral/Events/MetaModel/DuplicateModel.php +++ b/src/DcGeneral/Events/MetaModel/DuplicateModel.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Stefan Heimes * @author Christopher Boelter * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,6 +25,7 @@ use ContaoCommunityAlliance\DcGeneral\Event\PreDuplicateModelEvent; use MetaModels\IFactory; +use MetaModels\IMetaModel; /** * This class handles the paste into or after handling for variants. @@ -35,7 +37,7 @@ class DuplicateModel * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Create a new instance. @@ -60,7 +62,7 @@ public function handle(PreDuplicateModelEvent $event) $metaModel = $this->factory->getMetaModel($model->getProviderName()); - if (!$metaModel || !$metaModel->hasVariants()) { + if (null === $metaModel || !$metaModel->hasVariants()) { return; } diff --git a/src/DcGeneral/Events/MetaModel/PopulateAttributeEvent.php b/src/DcGeneral/Events/MetaModel/PopulateAttributeEvent.php index fe833f5fe..96bd13718 100644 --- a/src/DcGeneral/Events/MetaModel/PopulateAttributeEvent.php +++ b/src/DcGeneral/Events/MetaModel/PopulateAttributeEvent.php @@ -33,7 +33,7 @@ class PopulateAttributeEvent extends AbstractEnvironmentAwareEvent /** * The event name. */ - const NAME = 'metamodels.dc-general.events.metamodel.populate.attribute'; + public const NAME = 'metamodels.dc-general.events.metamodel.populate.attribute'; /** * The Attribute. diff --git a/src/DcGeneral/Events/MetaModel/PropertyOptionsProvider.php b/src/DcGeneral/Events/MetaModel/PropertyOptionsProvider.php index 57b73331a..1e5dbf53b 100644 --- a/src/DcGeneral/Events/MetaModel/PropertyOptionsProvider.php +++ b/src/DcGeneral/Events/MetaModel/PropertyOptionsProvider.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,6 +24,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\GetPropertyOptionsEvent; use MetaModels\Attribute\IAttribute; use MetaModels\DcGeneral\Data\Model; +use MetaModels\IItem; /** * This class retrieves the options of an attribute within a MetaModel unless someone else already provided them. @@ -45,7 +47,14 @@ public static function getPropertyOptions(GetPropertyOptionsEvent $event) if (!($model instanceof Model)) { return; } - $attribute = $model->getItem()->getAttribute($event->getPropertyName()); + + $propertyName = $event->getPropertyName(); + assert(\is_string($propertyName)); + + $item = $model->getItem(); + assert($item instanceof IItem); + + $attribute = $item->getAttribute($propertyName); if (!($attribute instanceof IAttribute)) { return; } @@ -53,7 +62,7 @@ public static function getPropertyOptions(GetPropertyOptionsEvent $event) try { $options = $attribute->getFilterOptions(null, false); } catch (\Exception $exception) { - $options = array('Error: ' . $exception->getMessage()); + $options = ['Error: ' . $exception->getMessage()]; } $event->setOptions($options); diff --git a/src/DcGeneral/Events/Table/FilterSetting/FilterSettingTypeRenderer.php b/src/DcGeneral/Events/Table/FilterSetting/FilterSettingTypeRenderer.php index 1f098c49e..f8d92fc75 100644 --- a/src/DcGeneral/Events/Table/FilterSetting/FilterSettingTypeRenderer.php +++ b/src/DcGeneral/Events/Table/FilterSetting/FilterSettingTypeRenderer.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,14 +13,18 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\DcGeneral\Events\Table\FilterSetting; +use Contao\System; +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use ContaoCommunityAlliance\DcGeneral\Contao\View\Contao2BackendView\Event\ModelToLabelEvent; +use MetaModels\CoreBundle\Assets\IconBuilder; use MetaModels\CoreBundle\EventListener\DcGeneral\Table\FilterSetting\AbstractFilterSettingTypeRenderer; use MetaModels\IMetaModelsServiceContainer; @@ -36,27 +40,37 @@ abstract class FilterSettingTypeRenderer extends AbstractFilterSettingTypeRender * The MetaModel service container. * * @var IMetaModelsServiceContainer + * + * @psalm-suppress DeprecatedInterface */ - private $serviceContainer; + private IMetaModelsServiceContainer $serviceContainer; /** * Create a new instance. * * @param IMetaModelsServiceContainer $serviceContainer The MetaModel service container. + * + * @psalm-suppress DeprecatedInterface + * @psalm-suppress DeprecatedMethod */ public function __construct(IMetaModelsServiceContainer $serviceContainer) { $this->serviceContainer = $serviceContainer; + $iconBuilder = System::getContainer()->get('metamodels.assets.icon_builder'); + assert($iconBuilder instanceof IconBuilder); + $scopeMatcher = System::getContainer()->get('cca.dc-general.scope-matcher'); + assert($scopeMatcher instanceof RequestScopeDeterminator); + parent::__construct( $serviceContainer->getFilterFactory(), $serviceContainer->getEventDispatcher(), - \System::getContainer()->get('metamodels.assets.icon_builder'), - \System::getContainer()->get('cca.dc-general.scope-matcher') + $iconBuilder, + $scopeMatcher ); $this->getServiceContainer()->getEventDispatcher()->addListener( ModelToLabelEvent::NAME, - array($this, 'modelToLabel') + [$this, 'modelToLabel'] ); } @@ -64,6 +78,8 @@ public function __construct(IMetaModelsServiceContainer $serviceContainer) * Retrieve the service container. * * @return IMetaModelsServiceContainer + * + * @psalm-suppress DeprecatedInterface */ protected function getServiceContainer() { diff --git a/src/Dca/Helper.php b/src/Dca/Helper.php index 9f9070005..3669256f3 100644 --- a/src/Dca/Helper.php +++ b/src/Dca/Helper.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,18 +19,22 @@ * @author Cliff Parnitzky * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\Dca; +use Contao\CoreBundle\Intl\Locales; +use Contao\Folder; use Contao\StringUtil; +use Contao\System; use ContaoCommunityAlliance\DcGeneral\DataDefinition\Definition\Properties\PropertyInterface; use ContaoCommunityAlliance\DcGeneral\EnvironmentInterface; use ContaoCommunityAlliance\Translator\TranslatorInterface; use MetaModels\IMetaModel; +use MetaModels\ITranslatedMetaModel; /** * This class is used as base class from dca handler classes for various callbacks. @@ -40,108 +44,130 @@ class Helper /** * Decode a language array. * - * @param array|string $varValue The value to decode. - * - * @param IMetaModel $objMetaModel The MetaModel holding the languages. + * @param array|string $varValue The value to decode. + * @param IMetaModel $metaModel The MetaModel holding the languages. * * @return string */ - public static function decodeLangArray($varValue, IMetaModel $objMetaModel) + public static function decodeLangArray($varValue, IMetaModel $metaModel) { $arrLangValues = StringUtil::deserialize($varValue); - if (!$objMetaModel->isTranslated()) { + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if (!($metaModel instanceof ITranslatedMetaModel) && !$metaModel->isTranslated(false)) { // If we have an array, return the first value and exit, if not an array, return the value itself. - return is_array($arrLangValues) - ? serialize($arrLangValues[key($arrLangValues)]) - : serialize($arrLangValues); + return \is_array($arrLangValues) + ? \serialize($arrLangValues[\key($arrLangValues)]) + : \serialize($arrLangValues); } // Sort like in MetaModel definition. - $arrLanguages = $objMetaModel->getAvailableLanguages(); + /** @psalm-suppress DeprecatedMethod */ + $arrLanguages = $metaModel->getAvailableLanguages(); $arrOutput = []; - if ($arrLanguages) { + if (null !== $arrLanguages) { foreach ($arrLanguages as $strLangCode) { - if (is_array($arrLangValues)) { - $varSubValue = $arrLangValues[$strLangCode]; + if (\is_array($arrLangValues)) { + $varSubValue = $arrLangValues[$strLangCode] ?? ''; } else { $varSubValue = $arrLangValues; } - if (is_array($varSubValue)) { - $arrOutput[] = array_merge($varSubValue, array('langcode' => $strLangCode)); + if (\is_array($varSubValue)) { + $arrOutput[] = \array_merge($varSubValue, array('langcode' => $strLangCode)); } else { $arrOutput[] = array('langcode' => $strLangCode, 'value' => $varSubValue); } } } - return serialize($arrOutput); + + return \serialize($arrOutput); } /** * Decode a language array. * - * @param array|string $varValue The value to decode. - * - * @param IMetaModel $objMetaModel The MetaModel holding the languages. + * @param array|string $varValue The value to decode. + * @param IMetaModel $metaModel The MetaModel holding the languages. * - * @return string + * @return string|array */ - public static function encodeLangArray($varValue, IMetaModel $objMetaModel) + public static function encodeLangArray($varValue, IMetaModel $metaModel) { // Not translated, make it a plain string. - if (!$objMetaModel->isTranslated()) { + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if (!($metaModel instanceof ITranslatedMetaModel) && !$metaModel->isTranslated(false)) { return $varValue; } + $arrLangValues = StringUtil::deserialize($varValue); $arrOutput = []; foreach ($arrLangValues as $varSubValue) { $strLangCode = $varSubValue['langcode']; unset($varSubValue['langcode']); - if (count($varSubValue) > 1) { + if (\count($varSubValue) > 1) { $arrOutput[$strLangCode] = $varSubValue; } else { - $arrKeys = array_keys($varSubValue); + $arrKeys = \array_keys($varSubValue); $arrOutput[$strLangCode] = $varSubValue[$arrKeys[0]]; } } - return serialize($arrOutput); + + return \serialize($arrOutput); } /** * Extract all languages from the MetaModel and return them as array. * * @param IMetaModel $metaModel The MetaModel to extract the languages from. - * * @param TranslatorInterface $translator The translator to use. * - * @return \string[] + * @return array */ private static function buildLanguageArray(IMetaModel $metaModel, TranslatorInterface $translator) { $languages = []; - foreach ((array) $metaModel->getAvailableLanguages() as $langCode) { - $languages[$langCode] = $translator->translate('LNG.' . $langCode, 'languages'); + if ($metaModel instanceof ITranslatedMetaModel) { + $intlLocales = System::getContainer()->get('contao.intl.locales'); + assert($intlLocales instanceof Locales); + $labels = $intlLocales->getLocales(); + foreach ($metaModel->getLanguages() as $langCode) { + $languages[$langCode] = $labels[$langCode]; + } + + return $languages; } - return $languages; + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if ($metaModel->isTranslated(false)) { + /** @psalm-suppress DeprecatedMethod */ + foreach ((array) $metaModel->getAvailableLanguages() as $langCode) { + $languages[$langCode] = $translator->translate('LNG.' . $langCode, 'contao_languages'); + } + return $languages; + } + + throw new \RuntimeException('Should not end up here.'); } /** * Create a widget for naming contexts. Use the language and translation information from the MetaModel. * * @param EnvironmentInterface $environment The environment. - * * @param PropertyInterface $property The property. - * * @param IMetaModel $metaModel The MetaModel. - * * @param string $languageLabel The label to use for the language indicator. - * * @param string $valueLabel The label to use for the input field. - * * @param bool $isTextArea If true, the widget will become a textarea, false otherwise. - * * @param array $arrValues The values for the widget, needed to highlight the fallback language. * * @return void @@ -155,7 +181,11 @@ public static function prepareLanguageAwareWidget( $isTextArea, $arrValues ) { - if (!$metaModel->isTranslated()) { + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if (!($metaModel instanceof ITranslatedMetaModel) && !$metaModel->isTranslated(false)) { $extra = $property->getExtra(); $extra['tl_class'] .= empty($extra['tl_class']) ? 'w50' : ' w50'; @@ -167,25 +197,30 @@ public static function prepareLanguageAwareWidget( return; } - $fallback = $metaModel->getFallbackLanguage(); - $languages = self::buildLanguageArray($metaModel, $environment->getTranslator()); + /** @psalm-suppress DeprecatedMethod */ + $fallback = $metaModel->getFallbackLanguage(); + + $translator = $environment->getTranslator(); + assert($translator instanceof TranslatorInterface); + + $languages = self::buildLanguageArray($metaModel, $translator); // Ensure we have values for all languages present. - if (array_diff_key($languages, $arrValues)) { - foreach (array_keys($languages) as $langCode) { + if (\array_diff_key($languages, $arrValues)) { + foreach (\array_keys($languages) as $langCode) { $arrValues[$langCode] = ''; } } $rowClasses = []; - foreach (array_keys($arrValues) as $langCode) { - $rowClasses[] = ($langCode == $fallback) ? 'fallback_language' : 'normal_language'; + foreach (\array_keys($arrValues) as $langCode) { + $rowClasses[] = ($langCode === $fallback) ? 'fallback_language' : 'normal_language'; } $extra = $property->getExtra(); $extra['minCount'] = - $extra['maxCount'] = count($languages); + $extra['maxCount'] = \count($languages); $extra['disableSorting'] = true; $extra['tl_class'] = 'clr w50'; $extra['columnFields'] = [ @@ -211,6 +246,7 @@ public static function prepareLanguageAwareWidget( ] ], ]; + $extra['useTranslator'] = true; $property ->setWidgetType('multiColumnWizard') @@ -221,7 +257,6 @@ public static function prepareLanguageAwareWidget( * Search all files with the given file extension below the given path. * * @param string $folder The folder to scan. - * * @param string $extension The file extension. * * @return array @@ -230,17 +265,19 @@ public static function searchFiles($folder, $extension) { $scanResult = []; $result = []; + $rootDir = System::getContainer()->getParameter('kernel.project_dir'); + assert(\is_string($rootDir)); // Check if we have a file or folder. - if (is_dir(TL_ROOT . '/' . $folder)) { - $scanResult = scan(TL_ROOT . '/' . $folder); + if (\is_dir($rootDir . '/' . $folder)) { + $scanResult = Folder::scan($rootDir . '/' . $folder); } // Run each value. foreach ($scanResult as $value) { - if (!is_file(TL_ROOT . '/' . $folder . '/' . $value)) { + if (!\is_file($rootDir . '/' . $folder . '/' . $value)) { $result += self::searchFiles($folder . '/' . $value, $extension); } else { - if (preg_match('/' . $extension . '$/i', $value)) { + if (\preg_match('/' . $extension . '$/i', $value)) { $result[$folder][$folder . '/' . $value] = $value; } } diff --git a/src/Events/CollectMetaModelTableNamesEvent.php b/src/Events/CollectMetaModelTableNamesEvent.php index f2298fa7d..78db11195 100644 --- a/src/Events/CollectMetaModelTableNamesEvent.php +++ b/src/Events/CollectMetaModelTableNamesEvent.php @@ -21,7 +21,7 @@ namespace MetaModels\Events; use MetaModels\IFactory; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered to collect the names of all known MetaModels. @@ -31,7 +31,7 @@ class CollectMetaModelTableNamesEvent extends Event /** * The event name. */ - const NAME = 'metamodels.metamodel.collect-table-names'; + public const NAME = 'metamodels.metamodel.collect-table-names'; /** * The factory calling the event. diff --git a/src/Events/CreateMetaModelEvent.php b/src/Events/CreateMetaModelEvent.php index d99401e3e..31c56f441 100644 --- a/src/Events/CreateMetaModelEvent.php +++ b/src/Events/CreateMetaModelEvent.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -22,17 +23,19 @@ use MetaModels\IFactory; use MetaModels\IMetaModel; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered for every metamodel when a factory wants to create an instance. + * + * @psalm-suppress PropertyNotSetInConstructor */ class CreateMetaModelEvent extends Event { /** * The event name. */ - const NAME = 'metamodels.metamodel.create'; + public const NAME = 'metamodels.metamodel.create'; /** * The factory calling the event. @@ -51,7 +54,7 @@ class CreateMetaModelEvent extends Event /** * The MetaModel instance being created. * - * @var IMetaModel + * @var IMetaModel|null */ protected $metaModel; @@ -59,7 +62,6 @@ class CreateMetaModelEvent extends Event * Create a new instance. * * @param IFactory $factory The MetaModel factory dispatching this event. - * * @param string $metaModelName The name of the MetaModel to be created. */ public function __construct($factory, $metaModelName) @@ -91,7 +93,7 @@ public function getMetaModelName() /** * Retrieve the MetaModel instance. * - * @return IMetaModel + * @return IMetaModel|null */ public function getMetaModel() { diff --git a/src/Events/CreatePropertyConditionEvent.php b/src/Events/CreatePropertyConditionEvent.php index e95c46699..d33aba011 100644 --- a/src/Events/CreatePropertyConditionEvent.php +++ b/src/Events/CreatePropertyConditionEvent.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,16 +23,18 @@ use ContaoCommunityAlliance\DcGeneral\DataDefinition\Palette\Condition\Property\PropertyConditionInterface; use MetaModels\IMetaModel; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is dispatched, whenever a MetaModels property condition shall be transformed into an object instance. * * @deprecated Implement proper factories and create conditions within there. + * + * @psalm-suppress PropertyNotSetInConstructor */ class CreatePropertyConditionEvent extends Event { - const NAME = 'metamodels.events.create-property-condition-event'; + public const NAME = 'metamodels.events.create-property-condition-event'; /** * The array containing the meta information for the instance. @@ -50,15 +53,14 @@ class CreatePropertyConditionEvent extends Event /** * The instance to be returned. * - * @var PropertyConditionInterface + * @var PropertyConditionInterface|null */ - protected $instance; + protected $instance = null; /** * Create a new instance. * * @param array $data The meta information for the instance. - * * @param IMetaModel $metaModel The MetaModel instance the condition applies to. */ public function __construct($data, IMetaModel $metaModel) @@ -90,7 +92,7 @@ public function getMetaModel() /** * Retrieve the instance. * - * @return PropertyConditionInterface + * @return PropertyConditionInterface|null */ public function getInstance() { diff --git a/src/Events/DatabaseBackedListener.php b/src/Events/DatabaseBackedListener.php index acd0bf8e6..2435dc76d 100644 --- a/src/Events/DatabaseBackedListener.php +++ b/src/Events/DatabaseBackedListener.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 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. @@ -16,7 +16,7 @@ * @author David Molineus * @author Cliff Parnitzky * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +26,7 @@ use Contao\System; use Doctrine\DBAL\Connection; use MetaModels\Attribute\Events\CollectMetaModelAttributeInformationEvent; +use MetaModels\Helper\LocaleUtil; use MetaModels\IMetaModel; use MetaModels\IMetaModelsServiceContainer; use MetaModels\ITranslatedMetaModel; @@ -36,6 +37,8 @@ /** * This is the information retriever database backend. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class DatabaseBackedListener { @@ -44,14 +47,14 @@ class DatabaseBackedListener * * @var Connection */ - private $database; + private Connection $database; /** * The event dispatcher. * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * All MetaModel instances created via this listener. @@ -60,7 +63,7 @@ class DatabaseBackedListener * * @var IMetaModel[] */ - private $instancesById = []; + private array $instancesById = []; /** * All MetaModel instances. @@ -69,35 +72,35 @@ class DatabaseBackedListener * * @var IMetaModel[] */ - private $instancesByTable = []; + private array $instancesByTable = []; /** * The table names. * * @var string[] */ - private $tableNames = []; + private array $tableNames = []; /** * Flag if the table names have already been collected. * * @var bool */ - private $tableNamesCollected = false; + private bool $tableNamesCollected = false; /** * All attribute information. * * @var array[] */ - private $attributeInformation = []; + private array $attributeInformation = []; /** * The system columns of MetaModels. * * @var string[] */ - private $systemColumns; + private array $systemColumns; /** * Create a new instance. @@ -119,10 +122,16 @@ public function __construct(Connection $database, EventDispatcherInterface $disp * @return IMetaModelsServiceContainer * * @deprecated The service container is deprecated and should not be used anymore. + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer() { - return System::getContainer()->get(MetaModelsServiceContainer::class); + /** @psalm-suppress DeprecatedClass */ + $serviceContainer = System::getContainer()->get(MetaModelsServiceContainer::class); + assert($serviceContainer instanceof IMetaModelsServiceContainer); + + return $serviceContainer; } /** @@ -135,7 +144,7 @@ public function getServiceContainer() public function getMetaModelNameFromId(GetMetaModelNameFromIdEvent $event) { $metaModelId = $event->getMetaModelId(); - if (array_key_exists($metaModelId, $this->instancesById)) { + if (\array_key_exists($metaModelId, $this->instancesById)) { $event->setMetaModelName($this->instancesById[$metaModelId]->getTableName()); return; @@ -156,10 +165,10 @@ public function getMetaModelNameFromId(GetMetaModelNameFromIdEvent $event) ->where('t.id=:id') ->setParameter('id', $metaModelId) ->setMaxResults(1) - ->execute() - ->fetch(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAssociative(); - if ($table) { + if (false !== $table) { $this->tableNames[$metaModelId] = $table['tableName']; $event->setMetaModelName($this->tableNames[$metaModelId]); } @@ -170,7 +179,6 @@ public function getMetaModelNameFromId(GetMetaModelNameFromIdEvent $event) * Determines the correct factory from a metamodel table name and creates an instance using the factory. * * @param CreateMetaModelEvent $event The event. - * * @param array $arrData The meta information for the MetaModel. * * @return bool @@ -190,16 +198,15 @@ protected function createInstanceViaLegacyFactory(CreateMetaModelEvent $event, $ // @codingStandardsIgnoreEnd $factoryClass = $GLOBALS['METAMODELS']['factories'][$name]; - $event->setMetaModel(call_user_func_array(array($factoryClass, 'createInstance'), array($arrData))); + $event->setMetaModel(\call_user_func_array([$factoryClass, 'createInstance'], [$arrData])); - return $event->getMetaModel() !== null; + return (bool) $event->getMetaModel(); } /** * Create a MetaModel instance with the given information. * * @param CreateMetaModelEvent $event The event. - * * @param array $arrData The meta information for the MetaModel. * * @return void @@ -209,14 +216,18 @@ protected function createInstanceViaLegacyFactory(CreateMetaModelEvent $event, $ */ protected function createInstance(CreateMetaModelEvent $event, $arrData) { - if (!$this->createInstanceViaLegacyFactory($event, $arrData)) { + if (false === $this->createInstanceViaLegacyFactory($event, $arrData)) { if ($arrData['translated']) { $metaModel = new TranslatedMetaModel($arrData, $this->dispatcher, $this->database); - $metaModel->selectLanguage(\str_replace('-', '_', $GLOBALS['TL_LANGUAGE'])); + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + $metaModel->selectLanguage(LocaleUtil::formatAsLocale($GLOBALS['TL_LANGUAGE'] ?? 'en')); } else { $metaModel = new MetaModel($arrData, $this->dispatcher, $this->database); } + + /** @psalm-suppress DeprecatedMethod */ $metaModel->setServiceContainer(function () { + /** @psalm-suppress DeprecatedMethod */ return $this->getServiceContainer(); }, false); $event->setMetaModel($metaModel); @@ -238,6 +249,7 @@ protected function createInstance(CreateMetaModelEvent $event, $arrData) public function createMetaModel(CreateMetaModelEvent $event) { if ($event->getMetaModel() !== null) { + /** @psalm-suppress DeprecatedMethod */ if (($metaModel = $event->getMetaModel()) instanceof ITranslatedMetaModel && $metaModel->isTranslated()) { // @codingStandardsIgnoreStart @\trigger_error( @@ -264,10 +276,10 @@ public function createMetaModel(CreateMetaModelEvent $event) ->where('t.tableName=:tableName') ->setParameter('tableName', $metaModelName) ->setMaxResults(1) - ->execute() - ->fetch(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAssociative(); - if ($table) { + if (false !== $table) { $table['system_columns'] = $this->systemColumns; $this->createInstance($event, $table); @@ -295,8 +307,8 @@ public function collectMetaModelTableNames(CollectMetaModelTableNamesEvent $even ->select('*') ->from('tl_metamodel', 't') ->orderBy('t.sorting') - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); foreach ($tables as $table) { $this->tableNames[$table['id']] = $table['tableName']; @@ -316,7 +328,7 @@ public function collectMetaModelTableNames(CollectMetaModelTableNamesEvent $even public function collectMetaModelAttributeInformation(CollectMetaModelAttributeInformationEvent $event) { $metaModelName = $event->getMetaModel()->getTableName(); - if (!array_key_exists($metaModelName, $this->attributeInformation)) { + if (!\array_key_exists($metaModelName, $this->attributeInformation)) { $attributes = $this ->database ->createQueryBuilder() @@ -325,8 +337,8 @@ public function collectMetaModelAttributeInformation(CollectMetaModelAttributeIn ->where('t.pid=:pid') ->setParameter('pid', $event->getMetaModel()->get('id')) ->orderBy('t.sorting') - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); $this->attributeInformation[$metaModelName] = []; foreach ($attributes as $attribute) { diff --git a/src/Events/GetMetaModelNameFromIdEvent.php b/src/Events/GetMetaModelNameFromIdEvent.php index 57f5fe3eb..4788dcd6f 100644 --- a/src/Events/GetMetaModelNameFromIdEvent.php +++ b/src/Events/GetMetaModelNameFromIdEvent.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. @@ -13,24 +13,27 @@ * @package MetaModels/core * @author Christian Schiffler * @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\Events; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered when a MetaModel id must get translated to a MetaModel name. + * + * @psalm-suppress PropertyNotSetInConstructor */ class GetMetaModelNameFromIdEvent extends Event { /** * The event name. */ - const NAME = 'metamodels.metamodel.get-metamodel-name-from-id'; + public const NAME = 'metamodels.metamodel.get-metamodel-name-from-id'; /** * The MetaModel id to be translated. @@ -42,9 +45,9 @@ class GetMetaModelNameFromIdEvent extends Event /** * The name of the MetaModel. * - * @var string + * @var string|null */ - protected $metaModelName; + protected $metaModelName = null; /** * Create a new instance. @@ -69,7 +72,7 @@ public function getMetaModelId() /** * Retrieve the MetaModel name. * - * @return string + * @return string|null */ public function getMetaModelName() { diff --git a/src/Events/MetaModelsBootEvent.php b/src/Events/MetaModelsBootEvent.php index 05acf7950..f23cf7613 100644 --- a/src/Events/MetaModelsBootEvent.php +++ b/src/Events/MetaModelsBootEvent.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,7 +25,7 @@ use Contao\System; use MetaModels\IMetaModelsServiceContainer; use MetaModels\MetaModelsServiceContainer; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered when a metamodels sub system is booted. @@ -43,6 +44,8 @@ class MetaModelsBootEvent extends Event * @return IMetaModelsServiceContainer * * @deprecated + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer(): IMetaModelsServiceContainer { @@ -52,6 +55,11 @@ public function getServiceContainer(): IMetaModelsServiceContainer E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - return System::getContainer()->get(MetaModelsServiceContainer::class); + + /** @psalm-suppress DeprecatedClass */ + $serviceContainer = System::getContainer()->get(MetaModelsServiceContainer::class); + assert($serviceContainer instanceof IMetaModelsServiceContainer); + + return $serviceContainer; } } diff --git a/src/Events/ParseItemEvent.php b/src/Events/ParseItemEvent.php index a9cfc11ef..0cefb61a0 100644 --- a/src/Events/ParseItemEvent.php +++ b/src/Events/ParseItemEvent.php @@ -22,7 +22,7 @@ use MetaModels\IItem; use MetaModels\Render\Setting\ICollection; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered when a MetaModels item is parsed. diff --git a/src/Events/RenderItemListEvent.php b/src/Events/RenderItemListEvent.php index 00ee9745d..62e87a668 100644 --- a/src/Events/RenderItemListEvent.php +++ b/src/Events/RenderItemListEvent.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,7 +23,7 @@ use MetaModels\ItemList; use MetaModels\Render\Template; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is fired when a MetaModels list get's rendered. @@ -34,35 +35,33 @@ class RenderItemListEvent extends Event * * @var ItemList */ - private $list; + private ItemList $list; /** * The list template being rendered. * * @var Template */ - private $template; + private Template $template; /** * The calling object (most likely a Module or ContentElement). * - * @var object + * @var object|null */ - private $caller; + private object|null $caller; /** * Create a new instance. * - * @param ItemList $list The item list getting rendered. - * - * @param Template $template The list template. - * - * @param object $caller The calling object (most likely a Module or ContentElement). + * @param ItemList $list The item list getting rendered. + * @param Template $template The list template. + * @param object|null $caller The calling object (most likely a Module or ContentElement). */ public function __construct(ItemList $list, Template $template, $caller = null) { - $this->template = $template; $this->list = $list; + $this->template = $template; $this->caller = $caller; } diff --git a/src/Exceptions/Database/ColumnDoesNotExistException.php b/src/Exceptions/Database/ColumnDoesNotExistException.php index 1e276662b..6a92e9cda 100644 --- a/src/Exceptions/Database/ColumnDoesNotExistException.php +++ b/src/Exceptions/Database/ColumnDoesNotExistException.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,12 +13,12 @@ * @package MetaModels/core * @author David Molineus * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ - namespace MetaModels\Exceptions\Database; /** @@ -27,19 +27,19 @@ class ColumnDoesNotExistException extends \RuntimeException { /** - * Create a new exception for a non existing table. + * Create a new exception for a non-existing table. * * @param string $columnName Column name. * @param string $tableName The table name. * @param int $code The optional Exception code. * @param \Exception $previous The optional previous throwable used for the exception chaining. * - * @return static + * @return self */ public static function withName($columnName, $tableName, $code = 0, $previous = null) { - return new static( - sprintf('Column "%s" does not exist on table "%s".', $columnName, $tableName), + return new self( + \sprintf('Column "%s" does not exist on table "%s".', $columnName, $tableName), $code, $previous ); diff --git a/src/Exceptions/Database/ColumnExistsException.php b/src/Exceptions/Database/ColumnExistsException.php index 37392f38d..d13184e5b 100644 --- a/src/Exceptions/Database/ColumnExistsException.php +++ b/src/Exceptions/Database/ColumnExistsException.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,12 +13,12 @@ * @package MetaModels/core * @author David Molineus * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ - namespace MetaModels\Exceptions\Database; /** @@ -34,12 +34,12 @@ class ColumnExistsException extends \RuntimeException * @param int $code The optional Exception code. * @param \Exception $previous The optional previous throwable used for the exception chaining. * - * @return static + * @return self */ public static function withName($columnName, $tableName, $code = 0, $previous = null) { - return new static( - sprintf('Column "%s" already exists on table "%s', $columnName, $tableName), + return new self( + \sprintf('Column "%s" already exists on table "%s', $columnName, $tableName), $code, $previous ); diff --git a/src/Exceptions/Database/InvalidColumnNameException.php b/src/Exceptions/Database/InvalidColumnNameException.php index e0dc73bb3..a7d933cee 100644 --- a/src/Exceptions/Database/InvalidColumnNameException.php +++ b/src/Exceptions/Database/InvalidColumnNameException.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author David Molineus * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,11 +33,11 @@ class InvalidColumnNameException extends \RuntimeException * @param int $code The optional Exception code. * @param \Exception $previous The optional previous throwable used for the exception chaining. * - * @return static + * @return self */ public static function invalidCharacters($columnName, $code = 0, $previous = null) { - return new static(sprintf('The column name "%s" is invalid.', $columnName), $code, $previous); + return new self(\sprintf('The column name "%s" is invalid.', $columnName), $code, $previous); } /** @@ -46,10 +47,10 @@ public static function invalidCharacters($columnName, $code = 0, $previous = nul * @param int $code The optional Exception code. * @param \Exception $previous The optional previous throwable used for the exception chaining. * - * @return static + * @return self */ public static function systemColumn($columnName, $code = 0, $previous = null) { - return new static(sprintf('The column name "%s" is reserved for system use.', $columnName), $code, $previous); + return new self(\sprintf('The column name "%s" is reserved for system use.', $columnName), $code, $previous); } } diff --git a/src/Exceptions/Database/InvalidTableNameException.php b/src/Exceptions/Database/InvalidTableNameException.php index 6ca8c4b70..d582fbb8b 100644 --- a/src/Exceptions/Database/InvalidTableNameException.php +++ b/src/Exceptions/Database/InvalidTableNameException.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author David Molineus * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,10 +33,10 @@ class InvalidTableNameException extends \RuntimeException * @param int $code The optional Exception code. * @param \Exception $previous The optional previous throwable used for the exception chaining. * - * @return static + * @return self */ public static function invalidCharacters($tableName, $code = 0, $previous = null) { - return new static(sprintf('The table name "%s" is invalid.', $tableName), $code, $previous); + return new self(\sprintf('The table name "%s" is invalid.', $tableName), $code, $previous); } } diff --git a/src/Exceptions/Database/TableDoesNotExistException.php b/src/Exceptions/Database/TableDoesNotExistException.php index a8e475b79..ad01f6adc 100644 --- a/src/Exceptions/Database/TableDoesNotExistException.php +++ b/src/Exceptions/Database/TableDoesNotExistException.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,12 +13,12 @@ * @package MetaModels/core * @author David Molineus * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ - namespace MetaModels\Exceptions\Database; /** @@ -33,10 +33,10 @@ class TableDoesNotExistException extends \RuntimeException * @param int $code The optional Exception code. * @param \Exception $previous The optional previous throwable used for the exception chaining. * - * @return static + * @return self */ public static function withName($tableName, $code = 0, $previous = null) { - return new static(sprintf('Table "%s" does not exist.', $tableName), $code, $previous); + return new self(\sprintf('Table "%s" does not exist.', $tableName), $code, $previous); } } diff --git a/src/Exceptions/Database/TableExistsException.php b/src/Exceptions/Database/TableExistsException.php index b604175ca..e29b573d6 100644 --- a/src/Exceptions/Database/TableExistsException.php +++ b/src/Exceptions/Database/TableExistsException.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,12 +13,12 @@ * @package MetaModels/core * @author David Molineus * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ - namespace MetaModels\Exceptions\Database; /** @@ -33,10 +33,10 @@ class TableExistsException extends \RuntimeException * @param int $code The optional Exception code. * @param \Exception $previous The optional previous throwable used for the exception chaining. * - * @return static + * @return self */ public static function withName($tableName, $code = 0, $previous = null) { - return new static(sprintf('Table "%s" already exists.', $tableName), $code, $previous); + return new self(\sprintf('Table "%s" already exists.', $tableName), $code, $previous); } } diff --git a/src/Exceptions/DifferentValuesException.php b/src/Exceptions/DifferentValuesException.php index d44dee875..9ec1dc620 100644 --- a/src/Exceptions/DifferentValuesException.php +++ b/src/Exceptions/DifferentValuesException.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -28,27 +29,27 @@ class DifferentValuesException extends \Exception /** * Two values are an array but differ in count. */ - const ARRAY_COUNT_MISMATCH = 1; + public const ARRAY_COUNT_MISMATCH = 1; /** * Two values are an array but differ in keys. */ - const ARRAY_KEY_MISMATCH = 1; + public const ARRAY_KEY_MISMATCH = 1; /** * Two values are an array but have a different value at a certain key. */ - const ARRAY_VALUE_MISMATCH = 1; + public const ARRAY_VALUE_MISMATCH = 1; /** * Two values differ in type. */ - const TYPE_MISMATCH = 1; + public const TYPE_MISMATCH = 1; /** * Two values are different. */ - const VALUE_MISMATCH = 1; + public const VALUE_MISMATCH = 1; /** * The expected value. @@ -75,13 +76,9 @@ class DifferentValuesException extends \Exception * Create a new instance. * * @param mixed $expected The expected value. - * * @param mixed $actual The actual value. - * * @param bool $strict Compared in strict mode. - * * @param string $message The Exception message to throw. - * * @param int $code The Exception code. * * @param \Exception $previous The previous exception used for the exception chaining. @@ -103,9 +100,7 @@ public function __construct($expected, $actual, $strict, $message = '', $code = * Check if the actual argument is the same as the expected. * * @param mixed $expected The expected value. - * * @param mixed $actual The actual value. - * * @param bool $strict Run in strict mode. * * @return void @@ -168,65 +163,59 @@ public function isStrict() */ public function getLongMessage($glue = ' ') { - $messages = array(); + $messages = []; $exception = $this; do { $messages[] = $exception->getMessage(); } while (null !== ($exception = $exception->getPrevious())); - return implode($glue, $messages); + return \implode($glue, $messages); } /** * Check if the actual argument is of type array and empty and the expected value is of type string and also empty. * * @param mixed $expected The expected value. - * * @param mixed $actual The actual value. * * @return bool */ - private static function isEmptyArrayEquivalent($expected, $actual) + private static function isEmptyArrayEquivalent(mixed $expected, mixed $actual): bool { - return (gettype($expected) == 'string') - && ((gettype($actual) == 'array') || (gettype($actual) == 'NULL')) - && empty($actual) - && empty($expected); + return ('' === $expected) && ([] === $actual || null === $actual); } /** * Check for differences in arrays. * * @param array $expected The expected value. - * * @param array $actual The actual value. - * * @param bool $strict Run in strict mode. * * @return void * * @throws \LogicException When the values differ. */ - private static function calculateArrayDiff($expected, $actual, $strict) + private static function calculateArrayDiff(array $expected, array $actual, bool $strict): void { - if (count($expected) !== count($actual)) { + if (\count($expected) !== count($actual)) { throw new \LogicException( - sprintf( + \sprintf( 'Array element count mismatch. Found %s, expected %s.', - count($actual), - count($expected) + \count($actual), + \count($expected) ), self::ARRAY_COUNT_MISMATCH ); } - reset($actual); + \reset($actual); foreach ($expected as $key => $value) { if ($key !== key($actual)) { throw new \LogicException( - sprintf( + \sprintf( 'Array key mismatch. Found %s, expected %s.', - key($actual), + \key($actual), $key ), self::ARRAY_KEY_MISMATCH @@ -237,15 +226,15 @@ private static function calculateArrayDiff($expected, $actual, $strict) self::calculateDiff($value, current($actual), $strict); } catch (\Exception $exception) { throw new \LogicException( - sprintf( + \sprintf( 'Array value mismatch for key %s.', - key($actual) + \key($actual) ), self::ARRAY_VALUE_MISMATCH, $exception ); } - next($actual); + \next($actual); } } @@ -253,48 +242,46 @@ private static function calculateArrayDiff($expected, $actual, $strict) * Helper to determine if two values are the same. * * @param mixed $expected The expected value. - * * @param mixed $actual The actual value. - * * @param bool $strict Run in strict mode. * * @return void * * @throws \LogicException When the values differ. */ - private static function calculateDiff($expected, $actual, $strict) + private static function calculateDiff(mixed $expected, mixed $actual, bool $strict): void { if ($expected === $actual) { return; } - if (gettype($expected) !== gettype($actual)) { - // Only exception of the rule: array values are transported as empty string. + if (\gettype($expected) !== \gettype($actual)) { + // Only exception to the rule: array values are transported as empty string. if (!$strict && self::isEmptyArrayEquivalent($expected, $actual)) { return; } throw new \LogicException( - sprintf( + \sprintf( 'Encountered type %s expected %s (Found %s, expected %s)', - gettype($actual), - gettype($expected), - var_export($actual, true), - var_export($expected, true) + \gettype($actual), + \gettype($expected), + \var_export($actual, true), + \var_export($expected, true) ), self::TYPE_MISMATCH ); } - if (is_array($expected)) { + if (\is_array($expected)) { self::calculateArrayDiff($expected, $actual, $strict); } throw new \LogicException( - sprintf( + \sprintf( 'Found %s expected %s', - var_export($actual, true), - var_export($expected, true) + \var_export($actual, true), + \var_export($expected, true) ), self::VALUE_MISMATCH ); diff --git a/src/Factory.php b/src/Factory.php index 3f537d597..70e02d654 100644 --- a/src/Factory.php +++ b/src/Factory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Maack * @author Sven Baumann - * @copyright 2012-2021 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 */ @@ -30,6 +31,8 @@ * This is the MetaModel factory interface. * * To create a MetaModel instance, call @link{MetaModelFactory::getMetaModel()} + * + * @psalm-suppress DeprecatedInterface */ class Factory implements IFactory { @@ -38,21 +41,23 @@ class Factory implements IFactory * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * The service container. * - * @var IMetaModelsServiceContainer + * @var IMetaModelsServiceContainer|null + * + * @psalm-suppress DeprecatedInterface */ - private $serviceContainer; + private IMetaModelsServiceContainer|null $serviceContainer = null; /** * The already translated MetaModel names. * * @var string[] */ - private $lookupMap = array(); + private array $lookupMap = []; /** * Create a new instance. @@ -67,11 +72,14 @@ public function __construct(EventDispatcherInterface $dispatcher) /** * Set the service container. * - * @param IMetaModelsServiceContainer $serviceContainer The service container to use. + * @param IMetaModelsServiceContainer $serviceContainer The service container to use. + * @param bool $deprecationNotice The flag to trigger error. * * @return Factory * * @deprecated The service container will get removed, use the symfony service container instead. + * + * @psalm-suppress DeprecatedInterface */ public function setServiceContainer(IMetaModelsServiceContainer $serviceContainer, $deprecationNotice = true) { @@ -95,9 +103,15 @@ public function setServiceContainer(IMetaModelsServiceContainer $serviceContaine * @return IMetaModelsServiceContainer * * @deprecated The service container will get removed, use the symfony service container instead. + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer() { + if (null === $this->serviceContainer) { + throw new \RuntimeException('Deprecated service container is not set anymore by default.'); + } + // @codingStandardsIgnoreStart @trigger_error( '"' .__METHOD__ . '" is deprecated - use the services from the service container.', @@ -116,8 +130,12 @@ public function translateIdToMetaModelName($metaModelId) $event = new GetMetaModelNameFromIdEvent($metaModelId); $this->dispatcher->dispatch($event, $event::NAME); + $translated = $event->getMetaModelName(); + if (null === $translated) { + throw new \RuntimeException('Failed to convert id ' . $metaModelId . ' to table name.'); + } - $this->lookupMap[$metaModelId] = $event->getMetaModelName(); + $this->lookupMap[$metaModelId] = $translated; } return $this->lookupMap[$metaModelId]; @@ -132,9 +150,7 @@ public function getMetaModel($metaModelName) $this->dispatcher->dispatch($event, $event::NAME); - $metaModel = $event->getMetaModel(); - - return $metaModel; + return $event->getMetaModel(); } /** diff --git a/src/Filter/Filter.php b/src/Filter/Filter.php index 98d7568ec..f1fa25d94 100644 --- a/src/Filter/Filter.php +++ b/src/Filter/Filter.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Maack * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -38,14 +39,14 @@ class Filter implements IFilter /** * The contained filter rules. * - * @var array + * @var list */ - protected $arrFilterRules = array(); + protected $arrFilterRules = []; /** * The cached result after this filter has been evaluated. * - * @var string[]|null + * @var list|null */ protected $arrMatches = null; @@ -56,9 +57,7 @@ class Filter implements IFilter */ public function __construct(IMetaModel $objMetaModel) { - if ($objMetaModel) { - $this->strMetaModel = $objMetaModel->getTableName(); - } + $this->strMetaModel = $objMetaModel->getTableName(); } /** @@ -70,7 +69,7 @@ public function __clone() { $this->arrMatches = null; $arrOld = $this->arrFilterRules; - $this->arrFilterRules = array(); + $this->arrFilterRules = []; foreach ($arrOld as $objFilterRule) { $this->addFilterRule(clone $objFilterRule); } @@ -83,9 +82,7 @@ public function __clone() */ public function createCopy() { - $objCopy = clone $this; - - return $objCopy; + return clone $this; } /** @@ -112,7 +109,6 @@ public function getMatchingIds() $arrIds = null; foreach ($this->arrFilterRules as $objFilterRule) { - /** @var IFilterRule $objFilterRule */ $arrRuleIds = $objFilterRule->getMatchingIds(); if ($arrRuleIds === null) { continue; @@ -122,13 +118,15 @@ public function getMatchingIds() $arrIds = $arrRuleIds; } else { // NOTE: all rules are implicitely "AND"-ed together. - $arrIds = array_intersect($arrIds, $arrRuleIds); - // When no ids are left any more, the result will stay empty, do not evaluate any further rules. - if (count($arrIds) == 0) { + $arrIds = \array_intersect($arrIds, $arrRuleIds); + // When no ids are left anymore, the result will stay empty, do not evaluate any further rules. + if (\count($arrIds) === 0) { break; } } } + $arrIds = (null !== $arrIds) ? \array_values($arrIds) : null; + $this->arrMatches = $arrIds; return $arrIds; diff --git a/src/Filter/FilterUrl.php b/src/Filter/FilterUrl.php index 7110a7f92..2f4782a9e 100644 --- a/src/Filter/FilterUrl.php +++ b/src/Filter/FilterUrl.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2023 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package MetaModels/core * @author Christian Schiffler * @author Ingolf Steinhardt - * @copyright 2012-2023 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,30 +28,30 @@ class FilterUrl /** * The page array. * - * @var string[] + * @var array */ - private $page = []; + private array $page = []; /** * All parameters to be used as GET parameters. * - * @var string[] + * @var array> */ - private $getParameters = []; + private array $getParameters = []; /** * All parameters to be used as slug. * - * @var string[] + * @var array */ - private $slugParameters = []; + private array $slugParameters = []; /** * Create a new instance. * - * @param string[] $page The page. - * @param string[] $getParameters The get parameters. - * @param string[] $slugParameters The slug parameters. + * @param array $page The page. + * @param array $getParameters The get parameters. + * @param array $slugParameters The slug parameters. */ public function __construct( array $page = [], @@ -60,10 +60,9 @@ public function __construct( ) { if (static::class !== __CLASS__) { // @codingStandardsIgnoreStart - @\trigger_deprecation( - 'metamodels/core', - '2.2', - static::class . ' should not extend ' . __CLASS__ . ' as it will become final in 3.0.' + @trigger_error( + static::class . ' should not extend ' . __CLASS__ . ' as it will become final in 3.0.', + E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd } @@ -94,7 +93,7 @@ public function clone(): self /** * Set the target page. * - * @param array $page The page. + * @param array $page The page. * * @return self */ @@ -108,7 +107,7 @@ public function setPage(array $page): self /** * Obtain the target page. * - * @return array + * @return array */ public function getPage(): array { @@ -148,7 +147,7 @@ public function getPageValue(string $name) } /** - * Add a slug parameter. + * Add a GET parameter. * * @param string $name The slug name. * @param string|list $value The slug value. @@ -157,8 +156,9 @@ public function getPageValue(string $name) */ public function setGet(string $name, $value): self { - if (empty($value)) { + if ([] === $value || '' === $value) { unset($this->getParameters[$name]); + return $this; } @@ -188,7 +188,7 @@ public function getGet(string $name) */ public function hasGet(string $name): bool { - return array_key_exists($name, $this->getParameters); + return \array_key_exists($name, $this->getParameters); } /** @@ -225,7 +225,7 @@ public function setSlug(string $name, string $value): self * * @param string $name The slug name. * - * @return string + * @return string|null */ public function getSlug(string $name): ?string { @@ -241,13 +241,13 @@ public function getSlug(string $name): ?string */ public function hasSlug(string $name): bool { - return array_key_exists($name, $this->slugParameters); + return \array_key_exists($name, $this->slugParameters); } /** * Obtain the slug parameters. * - * @return array + * @return array */ public function getSlugParameters(): array { diff --git a/src/Filter/FilterUrlBuilder.php b/src/Filter/FilterUrlBuilder.php index b7334c876..2b4573bd5 100644 --- a/src/Filter/FilterUrlBuilder.php +++ b/src/Filter/FilterUrlBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,82 +16,107 @@ * @author Ingolf Steinhardt * @author Stefan Heimes * @author Andreas Fischer - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\Filter; +use Contao\ArrayUtil; use Contao\Config; use Contao\CoreBundle\Framework\Adapter; -use Contao\CoreBundle\Routing\UrlGenerator; +use Contao\Model\Collection; use Contao\PageModel; use Contao\System; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; +use Symfony\Component\Routing\Generator\UrlGeneratorInterface; +use Symfony\Component\Routing\Route; /** * This class builds filter URLs. + * + * @psalm-type TFilterUrlOptions=array{ + * postAsSlug?: list, + * postAsGet?: list, + * preserveGet?: bool, + * removeGetOnSlug?: bool + * } + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class FilterUrlBuilder { /** * The URL generator. * - * @var UrlGenerator + * @var UrlGeneratorInterface */ - private $urlGenerator; + private UrlGeneratorInterface $urlGenerator; /** * The request stack. * * @var RequestStack */ - private $requestStack; + private RequestStack $requestStack; /** * Flag if the locale is prepended. * * @var bool */ - private $isLocalePrepended = true; + private bool $isLocalePrepended = true; /** * The Contao URL suffix. * * @var string */ - private $urlSuffix = '.html'; + private string $urlSuffix = '.html'; /** * The page model adapter. * - * @var Adapter|PageModel + * @var Adapter + */ + private Adapter $pageModelAdapter; + + /** + * Flag if legacy routing is active. + * + * @var bool */ - private $pageModelAdapter; + private bool $hasLegacyRouting; /** * Create a new instance. * - * @param UrlGenerator $urlGenerator The Contao URL generator. - * @param RequestStack $requestStack The request stack. - * @param bool $isLocalePrepended Flag if the locale is prepended to the URL. - * @param string $urlSuffix The URL suffix. - * @param Adapter $pageModelAdapter The page model adapter. + * @param UrlGeneratorInterface $urlGenerator The Contao URL generator. + * @param RequestStack $requestStack The request stack. + * @param bool $isLocalePrepended Flag if the locale is prepended to the URL. + * @param string $urlSuffix The URL suffix. + * @param Adapter $pageModelAdapter The page model adapter. + * @param bool $hasLegacyRouting Flag if legacy routing is active. */ public function __construct( - UrlGenerator $urlGenerator, + UrlGeneratorInterface $urlGenerator, RequestStack $requestStack, bool $isLocalePrepended, string $urlSuffix, - Adapter $pageModelAdapter + Adapter $pageModelAdapter, + bool $hasLegacyRouting ) { $this->urlGenerator = $urlGenerator; $this->requestStack = $requestStack; $this->isLocalePrepended = $isLocalePrepended; $this->urlSuffix = $urlSuffix; $this->pageModelAdapter = $pageModelAdapter; + $this->hasLegacyRouting = $hasLegacyRouting; } /** @@ -104,7 +129,7 @@ public function __construct( * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.Superglobals) */ - public function generate(FilterUrl $filterUrl) + public function generate(FilterUrl $filterUrl): string { $jumpTo = $filterUrl->getPage(); @@ -114,31 +139,28 @@ public function generate(FilterUrl $filterUrl) $jumpTo = $filterUrl->getPage(); } - $alias = $jumpTo['alias']; $parameters = $filterUrl->getGetParameters(); - $url = $alias; - if ($filterUrl->hasSlug('auto_item')) { - $url .= '/' . $this->encodeForAllowEncodedSlashes($filterUrl->getSlug('auto_item')); + $url = ''; + if ($filterUrl->hasSlug('auto_item') && '' !== ($slug = (string) $filterUrl->getSlug('auto_item'))) { + $url .= '/' . $this->encodeForAllowEncodedSlashes($slug); } - if (!empty($jumpTo['domain'])) { - $parameters['_domain'] = $jumpTo['domain']; - } - if (!empty($jumpTo['rootUseSSL'])) { - $parameters['_ssl'] = (bool) $jumpTo['rootUseSSL']; + if ($this->hasLegacyRouting) { + if (!empty($jumpTo['domain'])) { + $parameters['_domain'] = $jumpTo['domain']; + } + if (!empty($jumpTo['rootUseSSL'])) { + $parameters['_ssl'] = (bool) $jumpTo['rootUseSSL']; + } } - // Initialize with current language - locale is MANDATORY. - // See https://github.com/contao/contao/pull/4119 - $parameters['_locale'] = $GLOBALS['TL_LANGUAGE']; - if (null !== ($locale = $jumpTo['language'] ?? null)) { $parameters['_locale'] = $locale; } foreach ($filterUrl->getSlugParameters() as $name => $value) { - if (in_array($name, ['auto_item'])) { + if ($name === 'auto_item') { continue; } @@ -149,17 +171,23 @@ public function generate(FilterUrl $filterUrl) '/' . $this->encodeForAllowEncodedSlashes($value); } - return $this->urlGenerator->generate($url, $parameters); + if ($this->hasLegacyRouting) { + return $this->urlGenerator->generate($jumpTo['alias'] . $url, $parameters); + } + + $parameters['parameters'] = $url; + return $this->urlGenerator->generate('tl_page.' . $jumpTo['id'], $parameters); } /** * Generate a filter URL from the current request. * - * @param array $options The options for updating - for details see FilterUrlBuilder::addFromCurrentRequest(). + * @param TFilterUrlOptions|null $options The options for updating - for details + * see FilterUrlBuilder::addFromCurrentRequest(). * * @return FilterUrl */ - public function getCurrentFilterUrl($options = null): FilterUrl + public function getCurrentFilterUrl(array $options = null): FilterUrl { $this->addFromCurrentRequest($filterUrl = new FilterUrl(), $options); @@ -172,44 +200,72 @@ public function getCurrentFilterUrl($options = null): FilterUrl * This is mostly based on \Contao\Frontend::getPageIdFromUrl() but stripped off of some checks. * * Options may be: - * bool postAsSlug Fields of POST data that shall be added to the slug entries. - * default: [] - * bool postAsGet Fields of POST data that shall be added to the GET entries. - * default: [] - * bool preserveGet Flag if the GET parameters shall be added to the filter URL. - * default: true - * - * @param FilterUrl $filterUrl The filter URL to update. - * @param array $options The options for updating. + * bool postAsSlug Fields of POST data that shall be added to the slug entries. + * default: [] + * bool postAsGet Fields of POST data that shall be added to the GET entries. + * default: [] + * bool preserveGet Flag if the GET parameters shall be added to the filter URL. + * default: true + * bool removeGetOnSlug Flag to remove GET parameters from the filter URL when a same named slug parameter exists. + * default: true + * + * @param FilterUrl $filterUrl The filter URL to update. + * @param TFilterUrlOptions|null $options The options for updating. * * @return void */ - public function addFromCurrentRequest(FilterUrl $filterUrl, $options = null): void + public function addFromCurrentRequest(FilterUrl $filterUrl, array $options = null): void { if (null === $options) { $options = [ - 'postAsSlug' => [], - 'postAsGet' => [], - 'preserveGet' => true + 'postAsSlug' => [], + 'postAsGet' => [], + 'preserveGet' => true, + 'removeGetOnSlug' => true ]; } - $request = $this->requestStack->getMasterRequest(); + $request = $this->requestStack->getCurrentRequest(); if (null === $request) { return; } - if (isset($options['preserveGet'])) { + if ($options['preserveGet'] ?? true) { foreach ($request->query->all() as $name => $value) { $filterUrl->setGet($name, $value); } } - if (null === $fragments = $this->determineFragments($request)) { - $filterUrl->setPageValue('alias', 'index'); - $this->extractPostData($filterUrl, $options, $request); + if ($this->hasLegacyRouting) { + if (null === $fragments = $this->determineFragments($request)) { + $filterUrl->setPageValue('alias', 'index'); + $this->extractPostData($filterUrl, $options, $request); - return; + return; + } + } else { + $routeName = $this->determineRouteName($request); + + $filterUrl->setPageValue('id', \substr($routeName, 8)); + $requestUri = \rawurldecode(\substr($request->getPathInfo(), 1)); + + if (null === ($route = $request->attributes->get('_route_object'))) { + return; + } + assert($route instanceof Route); + + $pageModel = $route->getDefault('pageModel'); + assert($pageModel instanceof PageModel); + + $length = $pageModel->urlSuffix ? -\strlen($pageModel->urlSuffix) : null; + $start = ($pageModel->urlPrefix ? \strlen($pageModel->urlPrefix . '/') : 0) + + \strlen($pageModel->alias . '/'); + $fragments = \explode('/', \substr($requestUri, $start, $length)); + + if (1 === \count($fragments) % 2) { + \array_unshift($fragments, 'auto_item'); + } + \array_unshift($fragments, $pageModel->alias); } // If alias part is empty, this means we have the 'index' page. @@ -218,18 +274,22 @@ public function addFromCurrentRequest(FilterUrl $filterUrl, $options = null): vo } $filterUrl->setPageValue('alias', $fragments[0]); - // Add the fragments to the slug array + // Add the fragments to the slug array. for ($i = 1, $c = \count($fragments); $i < $c; $i += 2) { - // Skip key value pairs if the key is empty (see contao/core/#4702) + // Skip key value pairs if the key is empty (see contao/core/#4702). if ('' === $fragments[$i]) { continue; } // Decode slashes in slugs - They got encoded in generate() above. + $name = $this->decodeForAllowEncodedSlashes($fragments[$i]); $filterUrl->setSlug( - $this->decodeForAllowEncodedSlashes($fragments[$i]), + $name, $this->decodeForAllowEncodedSlashes($fragments[($i + 1)]) ); + if (($options['removeGetOnSlug'] ?? true) && $filterUrl->hasGet($name)) { + $filterUrl->setGet($name, ''); + } } $this->extractPostData($filterUrl, $options, $request); @@ -246,7 +306,7 @@ public function addFromCurrentRequest(FilterUrl $filterUrl, $options = null): vo */ private function encodeForAllowEncodedSlashes(string $value): string { - return str_replace(['/', '\\'], ['%2F', '%5C'], $value); + return \str_replace(['/', '\\'], ['%2F', '%5C'], $value); } /** @@ -260,7 +320,7 @@ private function encodeForAllowEncodedSlashes(string $value): string */ private function decodeForAllowEncodedSlashes(string $value): string { - return str_replace(['%2F', '%5C'], ['/', '\\'], $value); + return \str_replace(['%2F', '%5C'], ['/', '\\'], $value); } /** @@ -281,7 +341,7 @@ private function determineFragments(Request $request): ?array $fragments = null; // Use folder-style URLs - if (Config::get('folderUrl') && false !== strpos($requestUri, '/')) { + if (Config::get('folderUrl') && \str_contains($requestUri, '/')) { $fragments = $this->getFolderUrlFragments( $requestUri, $request->getHttpHost(), @@ -294,12 +354,12 @@ private function determineFragments(Request $request): ?array if ('/' === $requestUri) { return null; } - $fragments = explode('/', $requestUri); + $fragments = \explode('/', $requestUri); } // Add the second fragment as auto_item if the number of fragments is even if (Config::get('useAutoItem') && 0 === (\count($fragments) % 2)) { - array_insert($fragments, 1, ['auto_item']); + \array_splice($fragments, 1, 0, 'auto_item'); } $fragments = $this->getPageIdFromUrlHook($fragments); @@ -317,27 +377,29 @@ private function determineFragments(Request $request): ?array * * @param Request $request The request. * - * @return string + * @return string|null */ private function strippedUri(Request $request): ?string { // Strip leading slash. - if (null === $request || '' === $requestUri = rawurldecode(substr($request->getPathInfo(), 1))) { + if ('' === $requestUri = \rawurldecode(\substr($request->getPathInfo(), 1))) { return null; } if ($this->isLocalePrepended) { $matches = []; // Use the matches instead of substr() (thanks to Mario Müller) - if (preg_match('@^([a-z]{2}(-[A-Z]{2})?)/(.*)$@', $requestUri, $matches)) { + if (\preg_match('@^([a-z]{2}(-[A-Z]{2})?)/(.*)$@', $requestUri, $matches)) { $requestUri = $matches[3]; } } // Remove the URL suffix if not just a language root (e.g. en/) is requested - if ('' !== $this->urlSuffix && '' !== $requestUri && ( - !$this->isLocalePrepended || !preg_match('@^[a-z]{2}(-[A-Z]{2})?/$@', $requestUri) - )) { - $requestUri = substr($requestUri, 0, -\strlen($this->urlSuffix)); + if ( + '' !== $this->urlSuffix && '' !== $requestUri && ( + !$this->isLocalePrepended || !\preg_match('@^[a-z]{2}(-[A-Z]{2})?/$@', $requestUri) + ) + ) { + $requestUri = \substr($requestUri, 0, -\strlen($this->urlSuffix)); } return $requestUri; @@ -350,7 +412,7 @@ private function strippedUri(Request $request): ?string * @param string $host The host part of the current request. * @param string|null $locale The current locale or null if none requested. * - * @return array + * @return array|null * * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ @@ -358,34 +420,30 @@ private function getFolderUrlFragments(string $alias, string $host, string $loca { // Check if there are pages with a matching alias $pages = $this->getPageCandidates($alias); - if (null === $pages) { + if ([] === $pages) { return null; } // Look for a root page whose domain name matches the host name - if (isset($pages[$host])) { - $languages = $pages[$host]; - } else { + $languages = $pages[$host] // empty domain - $languages = $pages['*'] ?: []; - } + ?? ($pages['*'] ?: []); unset($pages); $pages = []; - $locale = ($locale) - ? \str_replace('_', '-', $locale) - : $locale; - if (!$this->isLocalePrepended) { - // Use the first result (see #4872) - $pages = current($languages); - } elseif ($locale && isset($languages[$locale])) { + // Use the first result (see #4872). + $pages = \current($languages); + if (false === $pages) { + $pages = []; + } + } elseif (null !== $locale && isset($languages[$locale])) { // Try to find a page matching the language parameter $pages = $languages[$locale]; } - // Return if there are no matches + // Return if there are no matches. if (empty($pages)) { return null; } @@ -394,12 +452,12 @@ private function getFolderUrlFragments(string $alias, string $host, string $loca $page = $pages[0]; // The request consists of the alias only - if ($alias == $page->alias) { + if ($alias === $page->alias) { $arrFragments = [$alias]; } else { // Remove the alias from the request string, explode it and then re-insert it at the beginning. - $arrFragments = explode('/', substr($alias, (\strlen($page->alias) + 1))); - array_unshift($arrFragments, $page->alias); + $arrFragments = \explode('/', \substr($alias, (\strlen($page->alias) + 1))); + \array_unshift($arrFragments, $page->alias); } return $arrFragments; @@ -410,24 +468,29 @@ private function getFolderUrlFragments(string $alias, string $host, string $loca * * @param string $alias The requested alias. * - * @return array|null + * @return array>> */ - private function getPageCandidates(string $alias) + private function getPageCandidates(string $alias): array { $aliases = [$alias]; // Compile all possible aliases by applying dirname() to the request. - while ('/' !== $alias && false !== strpos($alias, '/')) { + while ('/' !== $alias && \str_contains($alias, '/')) { $alias = \dirname($alias); $aliases[] = $alias; } - // Check if there are pages with a matching alias - $pages = $this->pageModelAdapter->findByAliases($aliases); - if (null === $pages) { - return null; - } + // Check if there are pages with a matching alias - sort by priority desc and alias* desc. + // *: You can assume that if folderurl is enabled, the lower hierarchy pages will have a + // longer alias string - hence descending sorting. + /** @psalm-suppress InternalMethod */ + $pages = $this->pageModelAdapter->findByAliases( + $aliases, + ['order' => 'tl_page.routePriority DESC, tl_page.alias DESC'] + ); + assert($pages instanceof Collection); + $arrPages = []; - // Order by domain and language + // Order by domain and language. while ($pages->next()) { /** @var PageModel $objModel */ $objModel = $pages->current(); @@ -435,11 +498,12 @@ private function getPageCandidates(string $alias) $domain = $objPage->domain ?: '*'; $arrPages[$domain][$objPage->rootLanguage][] = $objPage; - // Also store the fallback language + // Also store the fallback language. if ($objPage->rootIsFallback) { $arrPages[$domain]['*'][] = $objPage; } } + return $arrPages; } @@ -474,7 +538,7 @@ private function getPageIdFromUrlHook(?array $fragments): ?array * * @return void */ - private function extractPostData(FilterUrl $filterUrl, $options, Request $request): void + private function extractPostData(FilterUrl $filterUrl, array $options, Request $request): void { if (!$request->isMethod('POST')) { return; @@ -485,15 +549,34 @@ private function extractPostData(FilterUrl $filterUrl, $options, Request $reques } foreach ($request->request->all() as $name => $value) { - if (is_array($value)) { - $value = implode(',', $value); + if (\is_array($value)) { + $value = \implode(',', $value); } - if (in_array($name, $options['postAsSlug'])) { + if (\in_array($name, $options['postAsSlug'])) { $filterUrl->setSlug($name, $value); } - if (in_array($name, $options['postAsGet'])) { + if (\in_array($name, $options['postAsGet'])) { $filterUrl->setGet($name, $value); } } } + + /** + * Determine route name. + * + * @param Request $request + * + * @return string + */ + public function determineRouteName(Request $request): string + { + $pageModel = $request->attributes->get('pageModel'); + + return 'tl_page.' . match (true) { + ($pageModel instanceof PageModel) => $pageModel->id, + \is_int($pageModel) => (string) $pageModel, + default => + throw new \RuntimeException('Unknown page model encountered: ' . \get_debug_type($pageModel)), + }; + } } diff --git a/src/Filter/IFilter.php b/src/Filter/IFilter.php index 37f0c1d36..40589e07b 100644 --- a/src/Filter/IFilter.php +++ b/src/Filter/IFilter.php @@ -45,7 +45,7 @@ public function addFilterRule(IFilterRule $objFilterRule); /** * Narrow down the list of Ids that match the given filter. * - * @return string[]|null all matching Ids or null if all ids did match. + * @return list|null all matching Ids or null if all ids did match. */ public function getMatchingIds(); } diff --git a/src/Filter/IFilterRule.php b/src/Filter/IFilterRule.php index ee9475e9d..1c041d843 100644 --- a/src/Filter/IFilterRule.php +++ b/src/Filter/IFilterRule.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,7 +33,7 @@ interface IFilterRule * If no filtering was applied and therefore all ids shall be reported as valid, the return value of NULL is * allowed. * - * @return string[]|null + * @return list|null */ public function getMatchingIds(); } diff --git a/src/Filter/Rules/Comparing/GreaterThan.php b/src/Filter/Rules/Comparing/GreaterThan.php index 926a77cdf..82d4203f1 100644 --- a/src/Filter/Rules/Comparing/GreaterThan.php +++ b/src/Filter/Rules/Comparing/GreaterThan.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class GreaterThan implements IFilterRule * * @var IAttribute */ - protected $objAttribute = null; + protected $objAttribute; /** * The value to compare with. @@ -53,9 +54,7 @@ class GreaterThan implements IFilterRule * Creates an instance of this class. * * @param IAttribute $objAttribute The attribute that shall be searched. - * * @param mixed $varValue The value to compare against. - * * @param bool $blnInclusive If true, the passed value will be included in the check * and therefore make the check an equal-or-greater test. */ @@ -71,7 +70,7 @@ public function __construct(IAttribute $objAttribute, $varValue, $blnInclusive = * * If no entries have been found, the result is an empty array. * - * @return string[]|null + * @return list|null */ public function getMatchingIds() { diff --git a/src/Filter/Rules/Comparing/LessThan.php b/src/Filter/Rules/Comparing/LessThan.php index 09678fd32..536c29fac 100644 --- a/src/Filter/Rules/Comparing/LessThan.php +++ b/src/Filter/Rules/Comparing/LessThan.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class LessThan implements IFilterRule * * @var IAttribute */ - protected $objAttribute = null; + protected $objAttribute; /** * The value to compare with. @@ -53,9 +54,7 @@ class LessThan implements IFilterRule * Creates an instance of this class. * * @param IAttribute $objAttribute The attribute that shall be searched. - * * @param mixed $varValue The value to compare against. - * * @param bool $blnInclusive If true, the passed value will be included in the check * and therefore make the check an less-or-equal test. */ @@ -71,7 +70,7 @@ public function __construct(IAttribute $objAttribute, $varValue, $blnInclusive = * * If no entries have been found, the result is an empty array. * - * @return string[]|null + * @return list|null */ public function getMatchingIds() { diff --git a/src/Filter/Rules/Comparing/NotEqual.php b/src/Filter/Rules/Comparing/NotEqual.php index 7bf21ccb5..82b655a5c 100644 --- a/src/Filter/Rules/Comparing/NotEqual.php +++ b/src/Filter/Rules/Comparing/NotEqual.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -33,7 +34,7 @@ class NotEqual implements IFilterRule * * @var IAttribute */ - protected $objAttribute = null; + protected $objAttribute; /** * The value to compare with. @@ -46,7 +47,6 @@ class NotEqual implements IFilterRule * Creates an instance of this class. * * @param IAttribute $objAttribute The query that shall be executed. - * * @param mixed $varValue The value to compare against. */ public function __construct(IAttribute $objAttribute, $varValue) @@ -60,7 +60,7 @@ public function __construct(IAttribute $objAttribute, $varValue) * * If no entries have been found, the result is an empty array. * - * @return string[]|null + * @return list|null */ public function getMatchingIds() { diff --git a/src/Filter/Rules/Condition/ConditionAnd.php b/src/Filter/Rules/Condition/ConditionAnd.php index 2d9c68fc3..a65841986 100644 --- a/src/Filter/Rules/Condition/ConditionAnd.php +++ b/src/Filter/Rules/Condition/ConditionAnd.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -34,7 +34,7 @@ class ConditionAnd extends FilterRule * * @var IFilter[] */ - protected $arrChildFilters = array(); + protected $arrChildFilters = []; /** * Adds a child filter to this rule that will get evaluated when this rule is evaluated. @@ -56,15 +56,15 @@ public function addChild(IFilter $objFilter) public function getMatchingIds() { if (0 === count($this->arrChildFilters)) { - return array(); + return []; } $ids = null; foreach ($this->arrChildFilters as $objChildFilter) { $matchingIds = $objChildFilter->getMatchingIds(); - if (array() === $matchingIds) { + if ([] === $matchingIds) { // Empty array, no items allowed by this rule, break out. - return array(); + return []; } // If null => all items allowed by this rule => ignore it. @@ -77,9 +77,9 @@ public function getMatchingIds() continue; } - $ids = array_intersect($ids, $matchingIds); + $ids = \array_intersect($ids, $matchingIds); } - return is_array($ids) ? array_values($ids) : null; + return \is_array($ids) ? \array_values($ids) : null; } } diff --git a/src/Filter/Rules/Condition/ConditionOr.php b/src/Filter/Rules/Condition/ConditionOr.php index 558065726..e777da848 100644 --- a/src/Filter/Rules/Condition/ConditionOr.php +++ b/src/Filter/Rules/Condition/ConditionOr.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Maack * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,9 +33,9 @@ class ConditionOr extends FilterRule /** * The list of child filters that shall be evaluated. * - * @var IFilter[] + * @var list */ - protected $arrChildFilters = array(); + protected $arrChildFilters = []; /** * Flag determining if filtering shall return the first non-empty match. @@ -42,7 +43,7 @@ class ConditionOr extends FilterRule * This flag determines if the rule shall return the result of the first * child that returns at least one element (a return value of NULL from a * rule counts as "all ids" in this context and therefore is considered - * non empty per definition). + * non-empty per definition). * * @var boolean */ @@ -86,17 +87,17 @@ public function addChild(IFilter $objFilter) * evaluated, as the result set can not expand any further. * * Note: when "stopAfterMatch" has been set, the rule will stop processing - * also when the first rule returns a non empty result and return that + * also when the first rule returns a non-empty result and return that * result. * - * @return string[]|null + * @return list|null */ public function getMatchingIds() { - $arrIds = array(); + $arrIds = []; foreach ($this->arrChildFilters as $objChildFilter) { $arrChildMatches = $objChildFilter->getMatchingIds(); - // NULL => all items - for OR conditions, this can never be more than all so we are already satisfied here. + // NULL => all items - for OR conditions, this can never be more than all, so we are already satisfied here. if ($arrChildMatches === null) { return null; } @@ -106,10 +107,10 @@ public function getMatchingIds() } if ($arrChildMatches) { - $arrIds = array_merge($arrIds, $arrChildMatches); + $arrIds = \array_merge($arrIds, $arrChildMatches); } } - return array_unique($arrIds); + return \array_values(\array_unique($arrIds)); } } diff --git a/src/Filter/Rules/SearchAttribute.php b/src/Filter/Rules/SearchAttribute.php index 1daf83d04..18134e113 100644 --- a/src/Filter/Rules/SearchAttribute.php +++ b/src/Filter/Rules/SearchAttribute.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,32 +36,30 @@ class SearchAttribute extends FilterRule * * @var IAttribute|ITranslated|IComplex */ - protected $objAttribute = null; + protected $objAttribute; /** * The value to search for. * * @var string */ - protected $strValue = null; + protected $strValue = ''; /** * The valid languages to match (only used when searching a translated attribute). * - * @var array + * @var list */ - protected $arrValidLanguages = null; + protected $arrValidLanguages = []; /** * Creates an instance of a simple query filter rule. * - * @param IAttribute $objAttribute The attribute to be searched. - * - * @param string $strValue The value to be searched for. Wildcards (* and ? allowed). - * - * @param array $arrValidLanguages The list of valid languages to be searched in. + * @param IAttribute $objAttribute The attribute to be searched. + * @param string $strValue The value to be searched for. Wildcards (* and ? allowed). + * @param list $arrValidLanguages The list of valid languages to be searched in. */ - public function __construct($objAttribute, $strValue = '', $arrValidLanguages = array()) + public function __construct($objAttribute, $strValue = '', $arrValidLanguages = []) { parent::__construct(); $this->objAttribute = $objAttribute; diff --git a/src/Filter/Rules/SimpleQuery.php b/src/Filter/Rules/SimpleQuery.php index 0092c8c33..589a24acf 100644 --- a/src/Filter/Rules/SimpleQuery.php +++ b/src/Filter/Rules/SimpleQuery.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Cliff Parnitzky - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,47 +38,47 @@ class SimpleQuery extends FilterRule * * @var string */ - private $queryString; + private string $queryString; /** * The query parameters. * * @var array */ - private $params; + private array $params; /** * The name of the id column in the query. * * @var string */ - private $idColumn; + private string $idColumn; /** * The database instance to use. * * @var Connection */ - private $connection; + private Connection $connection; /** * The parameter types. * * @var array */ - private $types; + private array $types; /** * Create a rule instance from the passed query builder. * * @param QueryBuilder $builder The builder to extract query information from. - * * @param string $columnName The column to retrieve. * * @return SimpleQuery */ public static function createFromQueryBuilder(QueryBuilder $builder, $columnName = 'id') { + /** @psalm-suppress DeprecatedMethod */ return new self( $builder->getSQL(), $builder->getParameters(), @@ -108,9 +109,10 @@ public function __construct($queryString, $params = [], $idColumn = 'id', $conne $this->queryString = $queryString; $this->params = $params; - $this->idColumn = (string) $idColumn; - $this->connection = $this->sanitizeConnection($connection); - $this->types = $types; + $this->idColumn = $idColumn; + /** @psalm-suppress DeprecatedMethod */ + $this->connection = $this->sanitizeConnection($connection); + $this->types = $types; } /** @@ -120,8 +122,8 @@ public function getMatchingIds() { $matches = $this->connection->executeQuery($this->queryString, $this->params, $this->types); $ids = []; - foreach ($matches->fetchAll(\PDO::FETCH_ASSOC) as $value) { - $ids[] = $value[$this->idColumn]; + foreach ($matches->fetchAllAssociative() as $value) { + $ids[] = (string) $value[$this->idColumn]; } return $ids; @@ -130,7 +132,7 @@ public function getMatchingIds() /** * Sanitize the connection value * - * @param Connection|\Contao\Database $connection The connection value. + * @param Connection|\Contao\Database|null $connection The connection value. * * @return Connection * @@ -144,9 +146,10 @@ private function sanitizeConnection($connection) return $connection; } + // @codingStandardsIgnoreStart + // @codeCoverageIgnoreStart // BC layer - we used to accept a Contao database instance here. if ($connection instanceof Database) { - // @codingStandardsIgnoreStart @trigger_error( '"' . __METHOD__ . '" now accepts doctrine instances - ' . 'passing Contao database instances is deprecated.', @@ -155,8 +158,10 @@ private function sanitizeConnection($connection) // @codingStandardsIgnoreEnd $reflection = new \ReflectionProperty(Database::class, 'resConnection'); $reflection->setAccessible(true); + return $reflection->getValue($connection); } + if (null === $connection) { // @codingStandardsIgnoreStart @trigger_error( @@ -172,5 +177,7 @@ private function sanitizeConnection($connection) } return $connection; + // @codeCoverageIgnoreEnd + // @codingStandardsIgnoreEnd } } diff --git a/src/Filter/Rules/StaticIdList.php b/src/Filter/Rules/StaticIdList.php index 5d72de835..4a5b2ca23 100644 --- a/src/Filter/Rules/StaticIdList.php +++ b/src/Filter/Rules/StaticIdList.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Maack * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,14 +32,14 @@ class StaticIdList extends FilterRule /** * The static id list that shall be applied. * - * @var string[]|null + * @var list|null */ - protected $arrIds = array(); + protected $arrIds = []; /** * Create a new FilterRule instance. * - * @param string[]|null $arrIds Static list of ids that shall be returned as matches. + * @param list|null $arrIds Static list of ids that shall be returned as matches. */ public function __construct($arrIds) { diff --git a/src/Filter/Setting/AbstractFilterSettingTypeFactory.php b/src/Filter/Setting/AbstractFilterSettingTypeFactory.php index 5b6d0969d..211b127a7 100644 --- a/src/Filter/Setting/AbstractFilterSettingTypeFactory.php +++ b/src/Filter/Setting/AbstractFilterSettingTypeFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,18 +14,23 @@ * @author Christian Schiffler * @author Sven Baumann * @author David Molineus - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\Filter\Setting; +use MetaModels\Filter\Setting\IWithChildren; + /** * This is an abstract factory to query instances of attributes. * * Extend your own attribute factories from this class and register them when the create attribute factory event is * triggered. + * + * @psalm-suppress PropertyNotSetInConstructor */ abstract class AbstractFilterSettingTypeFactory implements IFilterSettingTypeFactory { @@ -34,21 +39,21 @@ abstract class AbstractFilterSettingTypeFactory implements IFilterSettingTypeFac * * @var string */ - private $typeName; + private string $typeName; /** * The name of the attribute class of this type. * - * @var string + * @var class-string */ - private $typeClass; + private string $typeClass; /** * The icon representing this filter setting type. * * @var string */ - private $typeIcon; + private string $typeIcon; /** * The maximum amount of children allowed. @@ -57,21 +62,21 @@ abstract class AbstractFilterSettingTypeFactory implements IFilterSettingTypeFac * * @var int|null */ - private $maxChildren; + private ?int $maxChildren = null; /** * List of valid attribute types that can be filtered with this filter. * - * @var string[] + * @var list|null */ - private $attributeTypes; + private ?array $attributeTypes = null; /** * Cache lookup variable. * * @var bool */ - private $isNestedType; + private ?bool $isNestedType = null; /** * Create a new instance. @@ -84,7 +89,7 @@ protected function __construct() /** * Set the type class. * - * @param string $typeClass The name of the class. + * @param class-string $typeClass The name of the class. * * @return AbstractFilterSettingTypeFactory */ @@ -98,7 +103,7 @@ protected function setTypeClass($typeClass) /** * Get the type class. * - * @return string|null + * @return class-string|null */ protected function getTypeClass(): ?string { @@ -164,11 +169,8 @@ public function createInstance($information, $filterSettings) */ public function isNestedType() { - if (!isset($this->isNestedType)) { - $this->isNestedType = in_array( - 'MetaModels\Filter\Setting\IWithChildren', - class_implements($this->typeClass, true) - ); + if (null === $this->isNestedType) { + $this->isNestedType = \in_array(IWithChildren::class, \class_implements($this->typeClass, true), true); } return $this->isNestedType; @@ -203,7 +205,7 @@ public function getMaxChildren() } /** - * Setup the allowance of attribute types to be added to this factory. + * Set up the allowance of attribute types to be added to this factory. * * This must be called before any calls to addKnownAttributeType() is allowed. * @@ -211,7 +213,8 @@ public function getMaxChildren() * * Either pass one parameter as array of string or pass 1 to n parameters as string. * - * @param string|string[] $initialType1toN One or more attribute type names to be available initially (optional). + * @param string|list $initialType1toN One or more attribute type names to be available + * initially (optional). * * @return AbstractFilterSettingTypeFactory * @@ -219,11 +222,12 @@ public function getMaxChildren() */ protected function allowAttributeTypes($initialType1toN = null) { - if (is_array($initialType1toN)) { + if (\is_array($initialType1toN)) { $this->attributeTypes = $initialType1toN; } else { - $this->attributeTypes = func_get_args(); + $this->attributeTypes = \func_get_args(); } + $this->attributeTypes = \array_values(\array_unique($this->attributeTypes)); return $this; } @@ -231,7 +235,7 @@ protected function allowAttributeTypes($initialType1toN = null) /** * Retrieve the list of known attribute types. * - * @return string[] The list of attribute names or null if no attributes are allowed. + * @return list|null The list of attribute names or null if no attributes are allowed. */ public function getKnownAttributeTypes() { @@ -249,11 +253,13 @@ public function getKnownAttributeTypes() */ public function addKnownAttributeType($typeName) { - if (!is_array($this->attributeTypes)) { + if (!\is_array($this->attributeTypes)) { throw new \LogicException('Filter setting ' . $this->typeClass . ' can not handle attributes.'); } - $this->attributeTypes[$typeName] = $typeName; + $this->attributeTypes[] = $typeName; + + $this->attributeTypes = \array_values(\array_unique($this->attributeTypes)); return $this; } diff --git a/src/Filter/Setting/Collection.php b/src/Filter/Setting/Collection.php index 60434a1f2..d854d3ea7 100644 --- a/src/Filter/Setting/Collection.php +++ b/src/Filter/Setting/Collection.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2020 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. @@ -17,7 +17,7 @@ * @author Sven Baumann * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2020 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,11 +32,13 @@ /** * This is the ICollection reference implementation. + * + * @psalm-suppress PropertyNotSetInConstructor */ class Collection implements ICollection { /** - * The additional meta data for this filter setting collection. + * The additional meta-data for this filter setting collection. * * @var array */ @@ -97,13 +99,7 @@ public function setMetaModel($metaModel): self */ public function getMetaModel() { - if ($this->metaModel) { - return $this->metaModel; - } - - throw new \RuntimeException( - sprintf('Error: Filter setting %d not attached to a MetaModel', $this->arrData['id']) - ); + return $this->metaModel; } /** @@ -127,7 +123,7 @@ public function addRules(IFilter $objFilter, $arrFilterUrl, $arrIgnoredFilter = { foreach ($this->arrSettings as $objSetting) { // If the setting is on the ignore list skip it. - if (in_array($objSetting->get('id'), $arrIgnoredFilter, false)) { + if (\in_array($objSetting->get('id'), $arrIgnoredFilter, false)) { continue; } @@ -145,7 +141,7 @@ public function generateFilterUrlFrom(IItem $objItem, IRenderSettings $objRender $filterUrl[] = $objSetting->generateFilterUrlFrom($objItem, $objRenderSetting); } - return [] === $filterUrl ? [] : array_merge(...$filterUrl); + return [] === $filterUrl ? [] : \array_merge(...$filterUrl); } /** @@ -158,7 +154,7 @@ public function getParameters() $parameters[] = $objSetting->getParameters(); } - return [] === $parameters ? [] : array_merge(...$parameters); + return [] === $parameters ? [] : \array_merge(...$parameters); } /** @@ -171,7 +167,7 @@ public function getParameterDCA() $parameters[] = $objSetting->getParameterDCA(); } - return [] === $parameters ? [] : array_merge(...$parameters); + return [] === $parameters ? [] : \array_merge(...$parameters); } /** @@ -181,14 +177,18 @@ public function getParameterFilterNames() { $parameters = []; foreach ($this->arrSettings as $objSetting) { - $parameters[] = $objSetting->getParameterFilterNames(); + $filterNames = $objSetting->getParameterFilterNames(); + $filterType = $objSetting->get('type'); + $parameters[] = \array_map(fn (string $name): string => $name . ' [' . $filterType . ']', $filterNames); } - return [] === $parameters ? [] : array_merge(...$parameters); + return [] === $parameters ? [] : \array_merge(...$parameters); } /** * {@inheritdoc} + * + * @SuppressWarnings(PHPMD.LongVariable) */ public function getParameterFilterWidgets( $arrFilterUrl, @@ -216,7 +216,7 @@ public function getParameterFilterWidgets( $setting->getParameterFilterWidgets($ids, $arrFilterUrl, $arrJumpTo, $objFrontendFilterOptions); } - return [] === $parameters ? [] : array_merge(...$parameters); + return [] === $parameters ? [] : \array_merge(...$parameters); } /** @@ -229,6 +229,6 @@ public function getReferencedAttributes() $attributes[] = $setting->getReferencedAttributes(); } - return [] === $attributes ? [] : array_merge(...$attributes); + return [] === $attributes ? [] : \array_merge(...$attributes); } } diff --git a/src/Filter/Setting/ConditionAndFilterSettingTypeFactory.php b/src/Filter/Setting/ConditionAndFilterSettingTypeFactory.php index 7e0ec06b2..b09585b73 100644 --- a/src/Filter/Setting/ConditionAndFilterSettingTypeFactory.php +++ b/src/Filter/Setting/ConditionAndFilterSettingTypeFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,13 +13,16 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\Filter\Setting; +use MetaModels\Filter\Setting\Condition\ConditionAnd; + /** * Attribute type factory for AND filter settings. */ @@ -35,7 +38,7 @@ public function __construct() $this ->setTypeName('conditionand') ->setTypeIcon('bundles/metamodelscore/images/icons/filter_and.png') - ->setTypeClass('MetaModels\Filter\Setting\Condition\ConditionAnd') + ->setTypeClass(ConditionAnd::class) ->allowAttributeTypes(); } } diff --git a/src/Filter/Setting/ConditionOrFilterSettingTypeFactory.php b/src/Filter/Setting/ConditionOrFilterSettingTypeFactory.php index 7f6d2637a..3f1ca0120 100644 --- a/src/Filter/Setting/ConditionOrFilterSettingTypeFactory.php +++ b/src/Filter/Setting/ConditionOrFilterSettingTypeFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,13 +13,16 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\Filter\Setting; +use MetaModels\Filter\Setting\Condition\ConditionOr; + /** * Attribute type factory for OR filter settings. */ @@ -35,7 +38,7 @@ public function __construct() $this ->setTypeName('conditionor') ->setTypeIcon('bundles/metamodelscore/images/icons/filter_or.png') - ->setTypeClass('MetaModels\Filter\Setting\Condition\ConditionOr') + ->setTypeClass(ConditionOr::class) ->allowAttributeTypes(); } } diff --git a/src/Filter/Setting/CustomSql.php b/src/Filter/Setting/CustomSql.php index d81693af1..d9e065130 100644 --- a/src/Filter/Setting/CustomSql.php +++ b/src/Filter/Setting/CustomSql.php @@ -44,6 +44,11 @@ /** * This filter condition generates a filter rule for a predefined SQL query. * The generated rule will only return ids that are returned from this query. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @psalm-suppress PropertyNotSetInConstructor */ class CustomSql implements ISimple, ServiceSubscriberInterface { @@ -52,49 +57,49 @@ class CustomSql implements ISimple, ServiceSubscriberInterface * * @var ICollection */ - private $collection = null; + private ICollection $collection; /** * The attributes of this filter setting. * - * @var array + * @var array */ - private $data = []; + private array $data; /** - * The filter params (should be an array or null). + * The filter params. * - * @var array + * @var array */ - private $filterParameters; + private array $filterParameters = []; /** * The query string. * * @var string */ - private $queryString; + private string $queryString; /** * The query parameters. * - * @var array + * @var list */ - private $queryParameter; + private array $queryParameter; /** * The service container. * * @var ContainerInterface */ - private $container; + private ContainerInterface $container; /** * Constructor - initialize the object and store the parameters. * - * @param ICollection $collection The parenting filter settings object. - * @param array $data The attributes for this filter setting. - * @param ContainerInterface $container The service container. + * @param ICollection $collection The parenting filter settings object. + * @param array $data The attributes for this filter setting. + * @param ContainerInterface $container The service container. * * @throws \InvalidArgumentException When a service is missing. */ @@ -104,14 +109,14 @@ public function __construct($collection, $data, ContainerInterface $container) $this->data = $data; $missing = []; - foreach (array_keys(self::getSubscribedServices()) as $serviceId) { + foreach (\array_keys(self::getSubscribedServices()) as $serviceId) { if (!$container->has($serviceId)) { $missing[] = $serviceId; } } if (!empty($missing)) { throw new \InvalidArgumentException( - 'The service container is missing the following services: ' . implode(', ', $missing) + 'The service container is missing the following services: ' . \implode(', ', $missing) ); } $this->container = $container; @@ -120,7 +125,7 @@ public function __construct($collection, $data, ContainerInterface $container) /** * Get the needed services. * - * @return array + * @return array */ public static function getSubscribedServices() { @@ -140,7 +145,7 @@ public static function getSubscribedServices() */ public function get($strKey) { - return isset($this->data[$strKey]) ? $this->data[$strKey] : null; + return $this->data[$strKey] ?? null; } /** @@ -154,9 +159,9 @@ public function prepareRules(IFilter $objFilter, $arrFilterUrl) $objFilter->addFilterRule($this->getFilterRule()); - unset($this->filterParameters); - unset($this->queryString); - unset($this->queryParameter); + $this->filterParameters = []; + $this->queryString = ''; + $this->queryParameter = []; } /** @@ -174,9 +179,9 @@ public function getParameters() { $arrParams = []; - preg_match_all('@\{\{param::filter\?([^}]*)\}\}@', $this->get('customsql'), $arrMatches); + \preg_match_all('@\{\{param::filter\?([^}]*)}}@', $this->get('customsql'), $arrMatches); foreach ($arrMatches[1] as $strQuery) { - parse_str($strQuery, $arrArgs); + \parse_str($strQuery, $arrArgs); if (isset($arrArgs['name'])) { $arrName = (array) $arrArgs['name']; $arrParams[] = $arrName[0]; @@ -204,6 +209,8 @@ public function getParameterFilterNames() /** * {@inheritdoc} + * + * @SuppressWarnings(PHPMD.LongVariable) */ public function getParameterFilterWidgets( $arrIds, @@ -253,17 +260,17 @@ private function getFilterRule() /** * Add parameters to the list. * - * @param array $parameters The parameters to add. + * @param list $parameters The parameters to add. * * @return void */ - private function addParameters(array $parameters) + private function addParameters(array $parameters): void { if (empty($parameters)) { return; } - $this->queryParameter = array_merge($this->queryParameter, $parameters); + $this->queryParameter = \array_merge($this->queryParameter, $parameters); } /** @@ -273,7 +280,7 @@ private function addParameters(array $parameters) * * @return void */ - private function addParameter(string $parameter) + private function addParameter(string $parameter): void { $this->queryParameter[] = $this->parseInsertTagsInternal($parameter); } @@ -283,7 +290,7 @@ private function addParameter(string $parameter) * * @return void */ - private function parseTable() + private function parseTable(): void { $this->queryString = $this->container->get(ReplaceTableName::class) ->replace($this->collection->getMetaModel()->getTableName(), $this->queryString); @@ -298,7 +305,7 @@ private function parseTable() * * @return mixed */ - private function getValueFromServiceContainer(string $valueName, array $arguments) + private function getValueFromServiceContainer(string $valueName, array $arguments): mixed { if (!empty($arguments['service'])) { $serviceName = $arguments['service']; @@ -307,8 +314,8 @@ private function getValueFromServiceContainer(string $valueName, array $argument } $service = $this->container->get(IMetaModelsServiceContainer::class)->getService($serviceName); - if (is_callable($service)) { - return call_user_func($service, $valueName, $arguments); + if (\is_callable($service)) { + return \call_user_func($service, $valueName, $arguments); } return 'NULL'; @@ -319,46 +326,29 @@ private function getValueFromServiceContainer(string $valueName, array $argument * * @param string $source The source to retrieve the value from. * Valid values are: ('get', 'post', 'cookie', 'session', 'filter' or 'container'). - * * @param string $valueName The name of the value in the source to retrieve. - * * @param array $arguments The arguments of the parameter. * * @return mixed */ - private function getValueFromSource(string $source, string $valueName, array $arguments) + private function getValueFromSource(string $source, string $valueName, array $arguments): mixed { - switch (strtolower($source)) { - case 'get': - case 'post': - case 'cookie': - case 'session': - return $this->executeInsertTagReplaceParam($source, $arguments); - - case 'filter': - if (is_array($this->filterParameters)) { - if (\array_key_exists($valueName, $this->filterParameters)) { - return $this->filterParameters[$valueName]; - } - - return null; - } - break; - - case 'container': - // @codingStandardsIgnoreStart - @trigger_error( - 'Getting filter values from the service container is deprecated, the container will get removed.', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - return $this->getValueFromServiceContainer($valueName, $arguments); - - default: + if (\strtolower($source) === 'container') { + // @codingStandardsIgnoreStart + @trigger_error( + 'Getting filter values from the service container is deprecated, the container will get removed.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + return $this->getValueFromServiceContainer($valueName, $arguments); } - // Unknown sources always resort to null. - return null; + return match (\strtolower($source)) { + 'get', 'post', 'cookie', 'session' => $this->executeInsertTagReplaceParam($source, $arguments), + 'filter' => $this->filterParameters[$valueName] ?? null, + // Unknown sources always resort to null. + default => null + }; } /** @@ -369,12 +359,12 @@ private function getValueFromSource(string $source, string $valueName, array $ar * * @return mixed|string */ - private function executeInsertTagReplaceParam(string $source, array $arguments) + private function executeInsertTagReplaceParam(string $source, array $arguments): mixed { $filteredArguments = \array_intersect_key($arguments, \array_flip(['name', 'default'])); $imploded = \array_reduce( \array_keys($filteredArguments), - function ($carry, $item) use ($filteredArguments) { + static function ($carry, $item) use ($filteredArguments) { return $carry . ($carry ? '&' : '') . $item . '=' . $filteredArguments[$item]; }, '' @@ -392,27 +382,26 @@ function ($carry, $item) use ($filteredArguments) { * Convert a parameter using an aggregate function. * * @param array $var The parameter value. - * * @param array $arguments The arguments of the parameter. * * @return string */ - private function convertParameterAggregate(array $var, array $arguments) + private function convertParameterAggregate(array $var, array $arguments): string { if (!empty($arguments['recursive'])) { - $var = \iterator_to_array( - new \RecursiveIteratorIterator( - new \RecursiveArrayIterator( - $var - ) - ) - ); + $var = \iterator_to_array(new \RecursiveIteratorIterator(new \RecursiveArrayIterator($var))); } - if (!$var) { + if ([] === $var) { return 'NULL'; } + if ($arguments['aggregate'] === 'list') { + $var = \array_merge( + ...\array_map(static fn (string $value): array => \explode(',', $value), \array_values($var)) + ); + } + if (!empty($arguments['key'])) { $var = \array_keys($var); } else { @@ -420,7 +409,7 @@ private function convertParameterAggregate(array $var, array $arguments) $var = \array_values($var); } - if ($arguments['aggregate'] == 'set') { + if (!\in_array($arguments['aggregate'], ['set', 'list'], true)) { $this->addParameter(implode(',', $var)); return '?'; @@ -440,19 +429,19 @@ private function convertParameterAggregate(array $var, array $arguments) */ private function convertParameter(string $strMatch): string { - list($strSource, $strQuery) = explode('?', $strMatch, 2); - parse_str($strQuery, $arrArgs); + [$strSource, $strQuery] = \explode('?', $strMatch, 2) + ['', '']; + \parse_str($strQuery, $arrArgs); $arrName = (array) $arrArgs['name']; - $var = $this->getValueFromSource($strSource, array_shift($arrName), $arrArgs); + $var = $this->getValueFromSource($strSource, \array_shift($arrName), $arrArgs); $index = 0; - $count = count($arrName); - while ($index < $count && is_array($var)) { + $count = \count($arrName); + while ($index < $count && \is_array($var)) { $var = $var[$arrName[$index++]]; } - if ($index != $count || $var === null) { + if ($index !== $count || $var === null) { if (\array_key_exists('default', $arrArgs) && (null !== $arrArgs['default'])) { $this->addParameter($arrArgs['default']); diff --git a/src/Filter/Setting/Events/CreateFilterSettingFactoryEvent.php b/src/Filter/Setting/Events/CreateFilterSettingFactoryEvent.php index add795f8d..a1f488fa7 100644 --- a/src/Filter/Setting/Events/CreateFilterSettingFactoryEvent.php +++ b/src/Filter/Setting/Events/CreateFilterSettingFactoryEvent.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2022 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2022 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,7 +22,7 @@ namespace MetaModels\Filter\Setting\Events; use MetaModels\Filter\Setting\IFilterSettingFactory; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered for every attribute factory instance that is created. diff --git a/src/Filter/Setting/FilterSettingFactory.php b/src/Filter/Setting/FilterSettingFactory.php index 43e6d3548..2e0d46075 100644 --- a/src/Filter/Setting/FilterSettingFactory.php +++ b/src/Filter/Setting/FilterSettingFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -30,6 +30,9 @@ /** * This is the filter settings factory interface. + * + * @psalm-suppress DeprecatedInterface + * @psalm-suppress PropertyNotSetInConstructor */ class FilterSettingFactory implements IFilterSettingFactory { @@ -37,6 +40,8 @@ class FilterSettingFactory implements IFilterSettingFactory * The event dispatcher. * * @var IMetaModelsServiceContainer + * + * @psalm-suppress DeprecatedInterface */ protected $serviceContainer; @@ -45,28 +50,28 @@ class FilterSettingFactory implements IFilterSettingFactory * * @var EventDispatcherInterface */ - private $eventDispatcher; + private EventDispatcherInterface $eventDispatcher; /** * The database connection. * * @var Connection */ - private $database; + private Connection $database; /** * The registered type factories. * * @var IFilterSettingTypeFactory[] */ - private $typeFactories; + private array $typeFactories; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Create a new instance. @@ -90,7 +95,7 @@ public function __construct( * * @deprecated The service container will get removed, use the symfony service container instead. */ - public function setServiceContainer(IMetaModelsServiceContainer $serviceContainer, $deprecationNotice = true) + public function setServiceContainer(IMetaModelsServiceContainer $serviceContainer, bool $deprecationNotice = true) { if ($deprecationNotice) { // @codingStandardsIgnoreStart @@ -209,9 +214,9 @@ private function collectRulesFor($parentSetting, $filterSettings) ->andWhere('t.enabled=1') ->orderBy('t.sorting', 'ASC') ->setParameter('pid', $parentSetting->get('id')) - ->execute(); + ->executeQuery(); - foreach ($childInformation->fetchAll(\PDO::FETCH_ASSOC) as $item) { + foreach ($childInformation->fetchAllAssociative() as $item) { $childSetting = $this->createSetting($item, $filterSettings); if ($childSetting) { $parentSetting->addChild($childSetting); @@ -237,9 +242,9 @@ public function collectRules($filterSettings) ->andWhere('t.enabled=1') ->orderBy('t.sorting', 'ASC') ->setParameter('fid', $filterSettings->get('id')) - ->execute(); + ->executeQuery(); - foreach ($information->fetchAll(\PDO::FETCH_ASSOC) as $item) { + foreach ($information->fetchAllAssociative() as $item) { $newSetting = $this->createSetting($item, $filterSettings); if ($newSetting) { $filterSettings->addSetting($newSetting); @@ -261,12 +266,9 @@ public function createCollection($settingId) ->where('t.id=:id') ->setMaxResults(1) ->setParameter('id', $settingId) - ->execute(); - if (!$query) { - throw new \RuntimeException('Could not retrieve filter setting'); - } + ->executeQuery(); - if (!empty($information = $query->fetch(\PDO::FETCH_ASSOC))) { + if (false !== ($information = $query->fetchAssociative())) { $metaModel = $this->factory->getMetaModel($this->factory->translateIdToMetaModelName($information['pid'])); if ($metaModel === null) { throw new \RuntimeException('Could not retrieve MetaModel ' . $information['pid']); @@ -279,7 +281,7 @@ public function createCollection($settingId) return $collection; } - return new Collection(array()); + return new Collection([]); } /** @@ -287,6 +289,6 @@ public function createCollection($settingId) */ public function getTypeNames() { - return array_keys($this->typeFactories); + return \array_keys($this->typeFactories); } } diff --git a/src/Filter/Setting/ICollection.php b/src/Filter/Setting/ICollection.php index 65fb126fc..c5adcc915 100644 --- a/src/Filter/Setting/ICollection.php +++ b/src/Filter/Setting/ICollection.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author David Maack * @author Stefan Heimes * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -55,9 +56,7 @@ public function getMetaModel(); * Generates all filter rules from the contained filter settings. * * @param IFilter $objFilter The filter object to add rules to. - * * @param array $arrFilterUrl The filter url to be applied. - * * @param array $arrIgnoredFilter An optional list with filter ids that should be ignored. * Defaults to empty array. * @@ -69,7 +68,6 @@ public function addRules(IFilter $objFilter, $arrFilterUrl, $arrIgnoredFilter = * Generate an filter url (aka jump to url) according to the contained filter rules. * * @param IItem $objItem The item from which the values shall be retrieved from. - * * @param IRenderSettings $objRenderSetting The render settings that hold the destination filter settings and * jumpTo page. * @@ -80,14 +78,14 @@ public function generateFilterUrlFrom(IItem $objItem, IRenderSettings $objRender /** * Retrieve a list of all registered parameters from the setting. * - * @return array + * @return list */ public function getParameters(); /** * Retrieve the names of all parameters for listing in frontend filter configuration. * - * @return string[] the parameters as array. parametername => label + * @return array the parameters as array. parametername => label */ public function getParameterFilterNames(); @@ -95,12 +93,12 @@ public function getParameterFilterNames(); * Retrieve a list of filter widgets for all registered parameters as form field arrays. * * @param array $arrFilterUrl The current filter url. - * * @param array $arrJumpTo The selected jump to page to use for link generating. - * * @param FrontendFilterOptions $objFrontendFilterOptions The frontend filter options to be passed to the widget. * - * @return array + * @return array + * + * @SuppressWarnings(PHPMD.LongVariable) */ public function getParameterFilterWidgets( $arrFilterUrl, @@ -118,7 +116,7 @@ public function getParameterDCA(); /** * Retrieve a list of all referenced attributes within the filter setting. * - * @return array + * @return list */ public function getReferencedAttributes(); } diff --git a/src/Filter/Setting/IFilterSettingFactory.php b/src/Filter/Setting/IFilterSettingFactory.php index 27a991a72..419ca053b 100644 --- a/src/Filter/Setting/IFilterSettingFactory.php +++ b/src/Filter/Setting/IFilterSettingFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -53,7 +53,7 @@ public function getTypeFactory($type); * * @param string $settingId The id of the ICollection. * - * @return ICollection The instance of the filter settings or null if not found. + * @return ICollection The instance of the filter settings. */ public function createCollection($settingId); diff --git a/src/Filter/Setting/IFilterSettingTypeFactory.php b/src/Filter/Setting/IFilterSettingTypeFactory.php index 30bb73a13..0f5d0281b 100644 --- a/src/Filter/Setting/IFilterSettingTypeFactory.php +++ b/src/Filter/Setting/IFilterSettingTypeFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -68,7 +69,7 @@ public function getMaxChildren(); /** * Retrieve the list of known attribute types. * - * @return string[] The list of attribute names or null if no attributes are allowed. + * @return list|null The list of attribute names or null if no attributes are allowed. */ public function getKnownAttributeTypes(); diff --git a/src/Filter/Setting/ISimple.php b/src/Filter/Setting/ISimple.php index 5f8998652..3d5e69665 100644 --- a/src/Filter/Setting/ISimple.php +++ b/src/Filter/Setting/ISimple.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author David Maack * @author Stefan Heimes * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -49,9 +50,8 @@ public function get($strKey); * A filter url hereby is a simple hash of name => value layout, it may eventually be interpreted * by attributes via IMetaModelAttribute::searchFor() method. * - * @param IFilter $objFilter The filter to append the rules to. - * - * @param string[] $arrFilterUrl The parameters to evaluate. + * @param IFilter $objFilter The filter to append the rules to. + * @param array $arrFilterUrl The parameters to evaluate. * * @return void */ @@ -64,17 +64,18 @@ public function prepareRules(IFilter $objFilter, $arrFilterUrl); * parameters have to be fetched. * * @param IItem $objItem The item to fetch the values from. - * * @param IRenderSettings $objRenderSetting The render setting to be applied. * - * @return array An array containing all the URL parameters needed by this filter setting. + * @return array An array containing all the URL parameters needed by this filter setting. + * + * @SuppressWarnings(PHPMD.LongVariable) */ public function generateFilterUrlFrom(IItem $objItem, IRenderSettings $objRenderSetting); /** * Retrieve a list of all registered parameters from the setting. * - * @return array + * @return list */ public function getParameters(); @@ -83,29 +84,28 @@ public function getParameters(); * * These parameters may be overridden by modules and content elements and the like. * - * @return array + * @return array */ public function getParameterDCA(); /** * Retrieve the names of all parameters for listing in frontend filter configuration. * - * @return string[] the parameters as array. parametername => label + * @return array the parameters as array. parametername => label */ public function getParameterFilterNames(); /** * Retrieve a list of filter widgets for all registered parameters as form field arrays. * - * @param string[]|null $arrIds The ids matching the current filter values. - * - * @param array $arrFilterUrl The current filter url. - * - * @param array $arrJumpTo The jumpTo page (array, row data from tl_page). - * + * @param list|null $arrIds The ids matching the current filter values. + * @param array $arrFilterUrl The current filter url. + * @param array $arrJumpTo The jumpTo page (array, row data from tl_page). * @param FrontendFilterOptions $objFrontendFilterOptions The frontend filter options. * - * @return array + * @return array + * + * @SuppressWarnings(PHPMD.LongVariable) */ public function getParameterFilterWidgets( $arrIds, @@ -117,7 +117,7 @@ public function getParameterFilterWidgets( /** * Retrieve a list of all referenced attributes within the filter setting. * - * @return array + * @return list */ public function getReferencedAttributes(); } diff --git a/src/Filter/Setting/Simple.php b/src/Filter/Setting/Simple.php index cddb8307c..d6adedafa 100644 --- a/src/Filter/Setting/Simple.php +++ b/src/Filter/Setting/Simple.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,7 +18,7 @@ * @author David Molineus * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -37,9 +37,13 @@ use MetaModels\IMetaModelsServiceContainer; use MetaModels\Render\Setting\ICollection as IRenderSettings; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; /** * Base class for filter setting implementation. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ abstract class Simple implements ISimple { @@ -48,14 +52,14 @@ abstract class Simple implements ISimple * * @var ICollection */ - private $collection = null; + private ICollection $collection; /** * The attributes of this filter setting. * * @var array */ - private $data = []; + private array $data = []; /** * The event dispatcher. @@ -69,7 +73,7 @@ abstract class Simple implements ISimple * * @var FilterUrlBuilder */ - private $filterUrlBuilder; + private FilterUrlBuilder $filterUrlBuilder; /** * Constructor - initialize the object and store the parameters. @@ -97,6 +101,7 @@ public function __construct( // @codingStandardsIgnoreEnd $eventDispatcher = System::getContainer()->get('event_dispatcher'); + assert($eventDispatcher instanceof EventDispatcherInterface); } if (null === $filterUrlBuilder) { @@ -107,6 +112,7 @@ public function __construct( ); // @codingStandardsIgnoreEnd $filterUrlBuilder = System::getContainer()->get('metamodels.filter_url'); + assert($filterUrlBuilder instanceof FilterUrlBuilder); } $this->eventDispatcher = $eventDispatcher; @@ -119,6 +125,8 @@ public function __construct( * @return IMetaModelsServiceContainer * * @deprecated Inject needed services via constructor or setter. + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer() { @@ -128,6 +136,7 @@ public function getServiceContainer() E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedMethod */ return $this->getMetaModel()->getServiceContainer(); } @@ -155,7 +164,7 @@ public function getEventDispatcher() */ public function get($strKey) { - return isset($this->data[$strKey]) ? $this->data[$strKey] : null; + return $this->data[$strKey] ?? null; } /** @@ -182,9 +191,7 @@ public function getMetaModel() * Returns if the given value is currently active in the given filter settings. * * @param array $arrWidget The widget information. - * * @param array $arrFilterUrl The filter url parameters to use. - * * @param string $strKeyOption The option value to determine. * * @return bool true If the given value is mentioned in the given filter parameters, false otherwise. @@ -200,7 +207,7 @@ protected function isActiveFrontendFilterValue($arrWidget, $arrFilterUrl, $strKe } if ($defaultValue = $this->get('defaultid')) { - return $strKeyOption == $defaultValue; + return $strKeyOption === $defaultValue; } return false; @@ -210,15 +217,13 @@ protected function isActiveFrontendFilterValue($arrWidget, $arrFilterUrl, $strKe * Translate an option to a proper url value to be used in the filter url. * * Overriding this method allows to toggle the value in the url in addition to extract - * or inject a value into an "combined" filter url parameter (like tags i.e.) + * or inject a value into a "combined" filter url parameter (like tags i.e.) * * @param array $arrWidget The widget information. - * * @param array $arrFilterUrl The filter url parameters to use. - * * @param string $strKeyOption The option value to determine. * - * @return string The filter url value to use for link gererating. + * @return string The filter url value to use for link generating. */ protected function getFrontendFilterValue($arrWidget, $arrFilterUrl, $strKeyOption) { @@ -233,12 +238,10 @@ protected function getFrontendFilterValue($arrWidget, $arrFilterUrl, $strKeyOpti * Add a parameter to the url, if it is auto_item, it will get prepended. * * @param string $url The url built so far. - * * @param string $name The parameter name. - * * @param mixed $value The parameter value. * - * @return string. + * @return string * * @deprecated Not in use anymore, use the FilterUrlBuilder. */ @@ -246,16 +249,16 @@ protected function addUrlParameter($url, $name, $value) { // @codingStandardsIgnoreStart @trigger_error( - sprintf('"%1$s" has been deprecated in favor of the "FilterUrlBuilder"', __METHOD__), + \sprintf('"%1$s" has been deprecated in favor of the "FilterUrlBuilder"', __METHOD__), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - if (is_array($value)) { - $value = implode(',', array_filter($value)); + if (\is_array($value)) { + $value = \implode(',', \array_filter($value)); } - $value = str_replace('%', '%%', urlencode($value)); + $value = \str_replace('%', '%%', \urlencode($value)); if (empty($value)) { return $url; @@ -274,7 +277,6 @@ protected function addUrlParameter($url, $name, $value) * Build the filter url based upon the fragments. * * @param array $fragments The parameters to be used in the Url. - * * @param string $searchKey The param key to handle for "this". * * @return string @@ -288,7 +290,7 @@ protected function buildFilterUrl($fragments, $searchKey) { // @codingStandardsIgnoreStart @trigger_error( - sprintf('"%1$s" has been deprecated in favor of the "FilterUrlBuilder"', __METHOD__), + \sprintf('"%1$s" has been deprecated in favor of the "FilterUrlBuilder"', __METHOD__), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd @@ -300,11 +302,11 @@ protected function buildFilterUrl($fragments, $searchKey) // The URL parameter concerning us will be masked via %s to be used later on in a sprintf(). foreach ($fragments as $key => $value) { // Skip the magic "language" parameter. - if (($key == 'language') && $GLOBALS['TL_CONFIG']['addLanguageToUrl']) { + if (($key === 'language') && $GLOBALS['TL_CONFIG']['addLanguageToUrl']) { continue; } - if ($key == $searchKey) { + if ($key === $searchKey) { if ($key !== 'auto_item') { $url .= '%s'; } else { @@ -312,6 +314,7 @@ protected function buildFilterUrl($fragments, $searchKey) } $found = true; } else { + /** @psalm-suppress DeprecatedMethod */ $url = $this->addUrlParameter($url, $key, $value); } } @@ -340,12 +343,9 @@ protected function buildFilterUrl($fragments, $searchKey) * * class The CSS class to use. Contains active if the option is active or is empty otherwise. * * @param array $arrWidget The widget information to use for value generating. - * * @param array $arrFilterUrl The filter url parameters to use. - * * @param array $arrJumpTo The jumpTo page to use for URL generating - if empty, the current * frontend page will get used. - * * @param bool $blnAutoSubmit Determines if the generated options/widgets shall perform auto submitting * or not. * @@ -366,25 +366,26 @@ protected function prepareFrontendFilterOptions($arrWidget, $arrFilterUrl, $arrJ $filterUrl = new FilterUrl($arrJumpTo); foreach ($arrFilterUrl as $name => $value) { - if (is_array($value)) { - $value = implode(',', array_filter($value)); + if (\is_array($value)) { + $value = \implode(',', \array_filter($value)); } $filterUrl->setSlug($name, (string) $value); } - $parameterName = $arrWidget['eval']['urlparam']; + $parameterName = $arrWidget['eval']['urlparam'] ?? ''; - if ($arrWidget['eval']['includeBlankOption']) { + if ((bool) ($arrWidget['eval']['includeBlankOption'] ?? false)) { $blnActive = $this->isActiveFrontendFilterValue($arrWidget, $arrFilterUrl, ''); $arrOptions[] = [ 'key' => '', - 'value' => ( - $arrWidget['eval']['blankOptionLabel'] - ? $arrWidget['eval']['blankOptionLabel'] - : $GLOBALS['TL_LANG']['metamodels_frontendfilter']['do_not_filter'] + 'value' => (string) ( + $arrWidget['eval']['blankOptionLabel'] + ?? ($GLOBALS['TL_LANG']['metamodels_frontendfilter']['do_not_filter'] ?? '') + ), + 'href' => $this->filterUrlBuilder->generate( + $filterUrl->clone()->setSlug($parameterName, '')->setGet($parameterName, '') ), - 'href' => $this->filterUrlBuilder->generate($filterUrl->clone()->setSlug($parameterName, '')), 'active' => $blnActive, 'class' => 'doNotFilter' . ($blnActive ? ' active' : ''), ]; @@ -397,7 +398,9 @@ protected function prepareFrontendFilterOptions($arrWidget, $arrFilterUrl, $arrJ $arrOptions[] = [ 'key' => $strKeyOption, 'value' => $strOption, - 'href' => $this->filterUrlBuilder->generate($filterUrl->clone()->setSlug($parameterName, $strValue)), + 'href' => $this->filterUrlBuilder->generate( + $filterUrl->clone()->setSlug($parameterName, $strValue)->setGet($parameterName, '') + ), 'active' => $blnActive, 'class' => StringUtil::standardize($strKeyOption) . ($blnActive ? ' active' : '') ]; @@ -433,6 +436,8 @@ protected function prepareFrontendFilterOptions($arrWidget, $arrFilterUrl, $arrJ * @SuppressWarnings(PHPMD.CamelCaseVariableName) * @SuppressWarnings(PHPMD.CyclomaticComplexity) * @SuppressWarnings(PHPMD.NPathComplexity) + * + * @SuppressWarnings(PHPMD.LongVariable) */ protected function prepareFrontendFilterWidget( array $arrWidget, @@ -440,7 +445,8 @@ protected function prepareFrontendFilterWidget( array $arrJumpTo, FrontendFilterOptions $objFrontendFilterOptions ): array { - $strClass = $GLOBALS['TL_FFL'][$arrWidget['inputType']]; + /** @var class-string|null $strClass */ + $strClass = $GLOBALS['TL_FFL'][$arrWidget['inputType']] ?? null; // No widget? no output! that's it. if (!$strClass) { @@ -461,29 +467,36 @@ protected function prepareFrontendFilterWidget( $this->eventDispatcher->dispatch($event, ContaoEvents::WIDGET_GET_ATTRIBUTES_FROM_DCA); - if ($objFrontendFilterOptions->isAutoSubmit() && TL_MODE == 'FE') { - $min = System::getContainer()->get('kernel')->isDebug() ? '' : '.min'; - $GLOBALS['TL_JAVASCRIPT']['metamodels'] = sprintf('bundles/metamodelscore/js/metamodels%s.js', $min); + $isFrontend = (bool) System::getContainer() + ->get('contao.routing.scope_matcher') + ?->isFrontendRequest( + System::getContainer()->get('request_stack')?->getCurrentRequest() ?? Request::create('') + ); + + if ($objFrontendFilterOptions->isAutoSubmit() && $isFrontend) { + $min = ((bool) System::getContainer()->get('kernel')?->isDebug()) ? '' : '.min'; + $GLOBALS['TL_JAVASCRIPT']['metamodels'] = \sprintf('bundles/metamodelscore/js/metamodels%s.js', $min); } - /** @var \Widget $objWidget */ + /** @psalm-suppress UnsafeInstantiation */ $objWidget = new $strClass($event->getResult()); $this->validateWidget($objWidget, $arrWidget['value']); $strField = $objWidget->generate(); + /** @psalm-suppress InvalidArgument - We assume the widget array is fine. */ return [ - 'cssID' => $arrWidget['eval']['cssID'], - 'class' => sprintf( + 'cssID' => $arrWidget['eval']['cssID'] ?? '', + 'class' => \sprintf( 'mm_%s %s%s%s%s', $arrWidget['inputType'], $arrWidget['eval']['urlparam'], (($arrWidget['value'] !== null) ? ' used' : ' unused'), ($objFrontendFilterOptions->isAutoSubmit() ? ' submitonchange' : ''), - $arrWidget['eval']['class'] + $arrWidget['eval']['class'] ?? '' ), 'label' => $objWidget->generateLabel(), 'legend' => $this->generateLegend($arrWidget), - 'hide_label' => $arrWidget['eval']['hide_label'], + 'hide_label' => $arrWidget['eval']['hide_label'] ?? false, 'formfield' => $strField, 'raw' => $arrWidget, 'urlparam' => $arrWidget['eval']['urlparam'], @@ -496,7 +509,7 @@ protected function prepareFrontendFilterWidget( 'count' => isset($arrWidget['count']) ? $arrWidget['count'] : null, 'showCount' => $objFrontendFilterOptions->isShowCountValues(), 'autosubmit' => $objFrontendFilterOptions->isAutoSubmit(), - 'urlvalue' => array_key_exists('urlvalue', $arrWidget) ? $arrWidget['urlvalue'] : $arrWidget['value'], + 'urlvalue' => \array_key_exists('urlvalue', $arrWidget) ? $arrWidget['urlvalue'] : $arrWidget['value'], 'errors' => $objWidget->hasErrors() ? $objWidget->getErrors() : [], 'used' => $arrWidget['value'] !== null ? true : false, 'urlfragment' => $objFrontendFilterOptions->getUrlFragment() @@ -537,6 +550,8 @@ public function getParameterFilterNames() /** * {@inheritdoc} + * + * @SuppressWarnings(PHPMD.LongVariable) */ public function getParameterFilterWidgets( $arrIds, @@ -559,7 +574,6 @@ public function getReferencedAttributes() * Validate the widget using the value. * * @param Widget $widget The widget to validate. - * * @param string|null $value The value to validate. * * @return void @@ -581,12 +595,12 @@ protected function validateWidget($widget, $value) /** * Generate legend. * - * @param array}> $arrWidget The widget information array. + * @param array{label: list} $arrWidget The widget information array. * * @return string */ protected function generateLegend($arrWidget): string { - return '' . $arrWidget['label'][0] . ''; + return '' . ($arrWidget['label'][0] ?? '') . ''; } } diff --git a/src/Filter/Setting/SimpleLookup.php b/src/Filter/Setting/SimpleLookup.php index e9b11e34c..09df3880d 100644 --- a/src/Filter/Setting/SimpleLookup.php +++ b/src/Filter/Setting/SimpleLookup.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author Stefan Heimes * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,17 +24,22 @@ namespace MetaModels\Filter\Setting; use Contao\StringUtil; +use Contao\System; use MetaModels\Attribute\IAttribute; use MetaModels\FrontendIntegration\FrontendFilterOptions; use MetaModels\IItem; use MetaModels\Filter\IFilter; use MetaModels\Filter\Rules\StaticIdList as FilterRuleStaticIdList; use MetaModels\Filter\Rules\SearchAttribute as FilterRuleSimpleLookup; +use MetaModels\IMetaModel; +use MetaModels\ITranslatedMetaModel; use MetaModels\Render\Setting\ICollection as IRenderSettings; +use Symfony\Contracts\Translation\TranslatorInterface; /** - * Filter setting implementation performing a search for a value on a - * configured attribute. + * Filter setting implementation performing a search for a value on a configured attribute. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) - revisit after removing bc layer. */ class SimpleLookup extends Simple { @@ -51,7 +56,13 @@ protected function getParamName() $objAttribute = $this->getFilteredAttribute(); if ($objAttribute) { - return $objAttribute->getColName(); + $paramName = $objAttribute->getColName(); + // Work around #1505. + if (\in_array($paramName, ['language', 'items'], true)) { + $paramName .= '__'; + } + + return $paramName; } return null; @@ -87,11 +98,9 @@ public function allowEmpty() /** * Internal helper function for descendant classes to retrieve the options. * - * @param IAttribute $objAttribute The attribute to search. - * - * @param string[]|null $arrIds The Id list of items for which to retrieve the options. - * - * @param array $arrCount If non null, the amount of matches will get returned. + * @param IAttribute $objAttribute The attribute to search. + * @param list|null $arrIds The Id list of items for which to retrieve the options. + * @param array $arrCount If non-null, the amount of matches will get returned. * * @return array */ @@ -106,20 +115,22 @@ protected function getParameterFilterOptions($objAttribute, $arrIds, &$arrCount // Remove empty values. foreach ($arrOptions as $mixOptionKey => $mixOptions) { // Remove html/php tags. - $mixOptions = strip_tags($mixOptions); - $mixOptions = trim($mixOptions); + $mixOptions = \strip_tags($mixOptions); + $mixOptions = \trim($mixOptions); - if ($mixOptions === '' || $mixOptions === null) { + if ($mixOptions === '') { unset($arrOptions[$mixOptionKey]); } } + // Sorting option for filter items. switch ($this->get('apply_sorting')) { case 'natsort_asc': \natcasesort($arrOptions); break; case 'natsort_desc': - \rsort($arrOptions, (SORT_NATURAL | SORT_FLAG_CASE)); + \natcasesort($arrOptions); + $arrOptions = \array_reverse($arrOptions, true); break; default: } @@ -146,26 +157,25 @@ public function prepareRules(IFilter $objFilter, $arrFilterUrl) $objAttribute = $this->getFilteredAttribute(); $strParam = $this->getParamName(); - if ($objAttribute && $strParam) { - if ($arrFilterValue = $this->determineFilterValue($arrFilterUrl, $strParam)) { - if ($objMetaModel->isTranslated() && $this->get('all_langs')) { - $arrLanguages = $objMetaModel->getAvailableLanguages(); - } else { - $arrLanguages = array($objMetaModel->getActiveLanguage()); - } - $objFilterRule = new FilterRuleSimpleLookup($objAttribute, $arrFilterValue, $arrLanguages); + if ($objAttribute && null !== $strParam) { + if (null !== ($arrFilterValue = $this->determineFilterValue($arrFilterUrl, $strParam))) { + $objFilterRule = new FilterRuleSimpleLookup( + $objAttribute, + $arrFilterValue, + $this->determineLanguages($objMetaModel) + ); $objFilter->addFilterRule($objFilterRule); + return; } // We found an attribute but no match in URL. So ignore this filter setting if allow_empty is set. if ($this->allowEmpty()) { - $objFilter->addFilterRule(new FilterRuleStaticIdList(null)); return; } } // Either no attribute found or no match in url, do not return anything. - $objFilter->addFilterRule(new FilterRuleStaticIdList(array())); + $objFilter->addFilterRule(new FilterRuleStaticIdList([])); } /** @@ -174,7 +184,9 @@ public function prepareRules(IFilter $objFilter, $arrFilterUrl) public function generateFilterUrlFrom(IItem $objItem, IRenderSettings $objRenderSetting) { if ($attribute = $this->getFilteredAttribute()) { - return [$this->getParamName() => $attribute->getFilterUrlValue($objItem->get($attribute->getColName()))]; + return [ + (string) $this->getParamName() => $attribute->getFilterUrlValue($objItem->get($attribute->getColName())) + ]; } return []; @@ -185,7 +197,7 @@ public function generateFilterUrlFrom(IItem $objItem, IRenderSettings $objRender */ public function getParameters() { - return ($strParamName = $this->getParamName()) ? array($strParamName) : array(); + return (null !== ($strParamName = $this->getParamName())) ? [$strParamName] : []; } /** @@ -202,20 +214,20 @@ public function getParameterDCA() } $objAttribute = $this->getFilteredAttribute(); - $arrOptions = $objAttribute->getFilterOptions(null, (bool) $this->get('onlyused')); + assert($objAttribute instanceof IAttribute); + + $arrOptions = $objAttribute->getFilterOptions(null, (bool) $this->get('onlyused')); + + $translator = System::getContainer()->get('translator'); + assert($translator instanceof TranslatorInterface); return [ - $this->getParamName() => [ - 'label' => [ - sprintf( - $GLOBALS['TL_LANG']['MSC']['metamodel_filtersettings_parameter']['simplelookup'][0], - $objAttribute->getName() - ), - sprintf( - $GLOBALS['TL_LANG']['MSC']['metamodel_filtersettings_parameter']['simplelookup'][1], - $objAttribute->getName() - ) - ], + (string) $this->getParamName() => [ + 'label' => $translator->trans( + 'simplelookup.label', + ['%id%' => $objAttribute->getName()], + 'metamodels_filter' + ), 'inputType' => 'select', 'options' => $arrOptions, 'eval' => [ @@ -231,13 +243,13 @@ public function getParameterDCA() */ public function getParameterFilterNames() { - if (($label = $this->getLabel()) && ($paramName = $this->getParamName())) { - return array( + if (null !== ($label = $this->getLabel()) && null !== ($paramName = $this->getParamName())) { + return [ $paramName => $label - ); + ]; } - return array(); + return []; } /** @@ -245,6 +257,7 @@ public function getParameterFilterNames() * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) + * @SuppressWarnings(PHPMD.LongVariable) */ public function getParameterFilterWidgets( $arrIds, @@ -254,14 +267,17 @@ public function getParameterFilterWidgets( ) { // If defined as static, return nothing as not to be manipulated via editors. if (!$this->enableFEFilterWidget()) { - return array(); + return []; } if (!($attribute = $this->getFilteredAttribute())) { - return array(); + return []; } - $GLOBALS['MM_FILTER_PARAMS'][] = $this->getParamName(); + $paramName = $this->getParamName(); + assert(\is_string($paramName)); + + $GLOBALS['MM_FILTER_PARAMS'][] = $paramName; $cssID = StringUtil::deserialize($this->get('cssID'), true); @@ -269,7 +285,7 @@ public function getParameterFilterWidgets( $arrWidget = [ 'label' => [ $this->getLabel(), - 'GET: ' . $this->getParamName() + 'GET: ' . $paramName ], 'inputType' => 'select', 'options' => $this->getParameterFilterOptions($attribute, $arrIds, $arrCount), @@ -283,9 +299,9 @@ public function getParameterFilterWidgets( ), 'blankOptionLabel' => $this->get('label_as_blankoption') ? $this->getLabel() - : $GLOBALS['TL_LANG']['metamodels_frontendfilter']['do_not_filter'], + : $GLOBALS['TL_LANG']['metamodels_frontendfilter']['do_not_filter'] ?? '', 'colname' => $attribute->getColname(), - 'urlparam' => $this->getParamName(), + 'urlparam' => $paramName, 'onlyused' => $this->get('onlyused'), 'onlypossible' => $this->get('onlypossible'), 'template' => $this->get('template'), @@ -295,15 +311,14 @@ public function getParameterFilterWidgets( ] ]; - return array - ( - $this->getParamName() => $this->prepareFrontendFilterWidget( + return [ + $paramName => $this->prepareFrontendFilterWidget( $arrWidget, $arrFilterUrl, $arrJumpTo, $objFrontendFilterOptions ) - ); + ]; } /** @@ -329,7 +344,7 @@ protected function getFilteredAttribute() return null; } - if ($attribute = $this->getMetaModel()->getAttributeById($attributeId)) { + if ($attribute = $this->getMetaModel()->getAttributeById((int) $attributeId)) { return $attribute; } @@ -350,6 +365,43 @@ private function determineFilterValue($filterValues, $valueName) return $this->get('defaultid'); } - return $filterValues[$valueName]; + return $filterValues[$valueName] ?? null; + } + + /** + * @return list + */ + private function determineLanguages(IMetaModel $metaModel): array + { + if ((bool) $this->get('all_langs')) { + if ($metaModel instanceof ITranslatedMetaModel) { + return \array_values(\array_filter($metaModel->getLanguages())); + } + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if ($metaModel->isTranslated(false)) { + /** @psalm-suppress DeprecatedMethod */ + return \array_values(\array_filter($metaModel->getAvailableLanguages() ?? [])); + } + + return []; + } + + if ($metaModel instanceof ITranslatedMetaModel) { + return \array_filter([$metaModel->getLanguage()]); + } + + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if ($metaModel->isTranslated(false)) { + /** @psalm-suppress DeprecatedMethod */ + return \array_filter([$metaModel->getActiveLanguage()]); + } + + return []; } } diff --git a/src/Filter/Setting/StaticIdListFilterSettingTypeFactory.php b/src/Filter/Setting/StaticIdListFilterSettingTypeFactory.php index cdefb4632..e74541c5d 100644 --- a/src/Filter/Setting/StaticIdListFilterSettingTypeFactory.php +++ b/src/Filter/Setting/StaticIdListFilterSettingTypeFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,6 +36,6 @@ public function __construct() $this ->setTypeName('idlist') ->setTypeIcon('bundles/metamodelscore/images/icons/filter_default.png') - ->setTypeClass('MetaModels\Filter\Setting\IdList'); + ->setTypeClass(IdList::class); } } diff --git a/src/Filter/Setting/WithChildren.php b/src/Filter/Setting/WithChildren.php index 3e34c9464..afb7521a4 100644 --- a/src/Filter/Setting/WithChildren.php +++ b/src/Filter/Setting/WithChildren.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author David Maack * @author Stefan Heimes * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +27,8 @@ use MetaModels\IItem; use MetaModels\Render\Setting\ICollection as IRenderSettings; +use function array_merge; + /** * Base implementation for settings that can contain children. */ @@ -36,7 +39,7 @@ abstract class WithChildren extends Simple implements IWithChildren * * @var ISimple[] */ - protected $arrChildren = array(); + protected $arrChildren = []; /** * {@inheritdoc} @@ -84,6 +87,8 @@ public function getParameterDCA() /** * {@inheritdoc} + * + * @SuppressWarnings(PHPMD.LongVariable) */ public function getParameterFilterWidgets( $arrIds, @@ -91,7 +96,7 @@ public function getParameterFilterWidgets( $arrJumpTo, FrontendFilterOptions $objFrontendFilterOptions ) { - $arrParams = array(); + $arrParams = []; foreach ($this->arrChildren as $objSetting) { $arrParams = array_merge( $arrParams, @@ -106,7 +111,7 @@ public function getParameterFilterWidgets( */ public function getParameterFilterNames() { - $arrParams = array(); + $arrParams = []; foreach ($this->arrChildren as $objSetting) { $arrParams = array_merge($arrParams, $objSetting->getParameterFilterNames()); } @@ -116,11 +121,11 @@ public function getParameterFilterNames() /** * Retrieve a list of all referenced attributes within the filter setting. * - * @return array + * @return list */ public function getReferencedAttributes() { - $arrAttributes = array(); + $arrAttributes = []; foreach ($this->arrChildren as $objSetting) { $arrAttributes = array_merge($arrAttributes, $objSetting->getReferencedAttributes()); } diff --git a/src/FrontendIntegration/Content/Filter.php b/src/FrontendIntegration/Content/Filter.php index 820d9b601..36c061d09 100644 --- a/src/FrontendIntegration/Content/Filter.php +++ b/src/FrontendIntegration/Content/Filter.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. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Andreas Nölke * @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 */ @@ -25,6 +26,9 @@ /** * Content element for FE-filtering. + * + * @psalm-suppress DeprecatedClass + * @psalm-suppress PropertyNotSetInConstructor */ class Filter extends HybridFilterBlock { diff --git a/src/FrontendIntegration/Content/FilterClearAll.php b/src/FrontendIntegration/Content/FilterClearAll.php index 17eb72ba9..2b3540033 100644 --- a/src/FrontendIntegration/Content/FilterClearAll.php +++ b/src/FrontendIntegration/Content/FilterClearAll.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 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. @@ -16,7 +16,7 @@ * @author Andreas Nölke * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,6 +27,10 @@ /** * Content element clearing the FE-filter. + * + * @psalm-suppress DeprecatedClass + * + * @psalm-suppress PropertyNotSetInConstructor */ class FilterClearAll extends HybridFilterClearAll { diff --git a/src/FrontendIntegration/Content/ModelList.php b/src/FrontendIntegration/Content/ModelList.php index d59bf4dd2..a8cd8e795 100644 --- a/src/FrontendIntegration/Content/ModelList.php +++ b/src/FrontendIntegration/Content/ModelList.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. @@ -19,7 +19,7 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author Richard Henkenjohann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,6 +32,9 @@ * Implementation of the MetaModel content element. * * @deprecated We switched to fragments {@see ItemListController.php} in MetaModels 2.2. To be removed in MetaModels 3. + * + * @psalm-suppress DeprecatedClass + * @psalm-suppress PropertyNotSetInConstructor */ class ModelList extends HybridList { diff --git a/src/FrontendIntegration/FrontendFilter.php b/src/FrontendIntegration/FrontendFilter.php index 7668291d9..7b9d25845 100644 --- a/src/FrontendIntegration/FrontendFilter.php +++ b/src/FrontendIntegration/FrontendFilter.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,7 +20,7 @@ * @author David Molineus * @author Marc Reimann * @author Richard Henkenjohann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,19 +28,28 @@ namespace MetaModels\FrontendIntegration; use Contao\CoreBundle\Exception\RedirectResponseException; +use Contao\FrontendTemplate; use Contao\Input; use Contao\System; use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; use ContaoCommunityAlliance\Contao\Bindings\Events\Controller\RedirectEvent; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Exception; use MetaModels\Filter\FilterUrl; use MetaModels\Filter\FilterUrlBuilder; use MetaModels\FrontendIntegration\Content\FilterClearAll as ContentElementFilterClearAll; use MetaModels\FrontendIntegration\Module\FilterClearAll as ModuleFilterClearAll; +use RuntimeException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * FE-filtering for Contao MetaModels. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @psalm-suppress PropertyNotSetInConstructor */ class FrontendFilter { @@ -63,20 +72,20 @@ class FrontendFilter * * @var Connection */ - private $connection; + private Connection $connection; /** * The filter URL builder. * * @var FilterUrlBuilder */ - private $filterUrlBuilder; + private FilterUrlBuilder $filterUrlBuilder; /** * FrontendFilter constructor. * - * @param Connection $connection Database connection. - * @param FilterUrlBuilder $filterUrlBuilder The filter URL builder. + * @param Connection|null $connection Database connection. + * @param FilterUrlBuilder|null $filterUrlBuilder The filter URL builder. */ public function __construct(Connection $connection = null, FilterUrlBuilder $filterUrlBuilder = null) { @@ -88,7 +97,10 @@ public function __construct(Connection $connection = null, FilterUrlBuilder $fil ); // @codingStandardsIgnoreEnd $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); } + $this->connection = $connection; + if (null === $filterUrlBuilder) { // @codingStandardsIgnoreStart @trigger_error( @@ -97,9 +109,8 @@ public function __construct(Connection $connection = null, FilterUrlBuilder $fil ); // @codingStandardsIgnoreEnd $filterUrlBuilder = System::getContainer()->get('metamodels.filter_url'); + assert($filterUrlBuilder instanceof FilterUrlBuilder); } - - $this->connection = $connection; $this->filterUrlBuilder = $filterUrlBuilder; } @@ -110,7 +121,10 @@ public function __construct(Connection $connection = null, FilterUrlBuilder $fil */ protected function getDispatcher() { - return System::getContainer()->get('event_dispatcher'); + $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + + return $dispatcher; } /** @@ -124,7 +138,8 @@ public function getMetaModelFrontendFilter(HybridFilterBlock $objFilterConfig) { $this->objFilterConfig = $objFilterConfig; - $this->formId .= $this->objFilterConfig->id; + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $this->formId .= $this->objFilterConfig->metamodel_fef_id ?: $this->objFilterConfig->id; return $this->getFilters(); } @@ -144,7 +159,7 @@ protected function getJumpToUrl($arrParams) { // @codingStandardsIgnoreStart @trigger_error( - sprintf('"%1$s" has been deprecated in favor of the new "FilterUrlBuilder"', __METHOD__), + \sprintf('"%1$s" has been deprecated in favor of the new "FilterUrlBuilder"', __METHOD__), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd @@ -152,29 +167,29 @@ protected function getJumpToUrl($arrParams) $strFilterAction = ''; foreach ($arrParams as $strName => $varParam) { // Skip the magic "language" parameter. - if (($strName == 'language') && $GLOBALS['TL_CONFIG']['addLanguageToUrl']) { + if (($strName === 'language') && $GLOBALS['TL_CONFIG']['addLanguageToUrl']) { continue; } $strValue = $varParam; - if (is_array($varParam)) { - $strValue = implode(',', array_filter($varParam)); + if (\is_array($varParam)) { + $strValue = \implode(',', \array_filter($varParam)); } - if (strlen($strValue)) { + if (\strlen($strValue)) { // Shift auto_item to the front. if ($strName == 'auto_item') { - $strFilterAction = '/' . rawurlencode(rawurlencode($strValue)) . $strFilterAction; + $strFilterAction = '/' . \rawurlencode(\rawurlencode($strValue)) . $strFilterAction; continue; } - $strFilterAction .= sprintf( + $strFilterAction .= \sprintf( $GLOBALS['TL_CONFIG']['disableAlias'] ? '&%s=%s' : '/%s/%s', - rawurlencode($strName), + \rawurlencode($strName), // Double rawurlencode to encode all special characters. // Look at http://php.net/manual/en/function.rawurlencode.php . - rawurlencode(rawurlencode($strValue)) + \rawurlencode(\rawurlencode($strValue)) ); } } @@ -196,7 +211,7 @@ protected function redirectPost($arrParams) { // @codingStandardsIgnoreStart @trigger_error( - sprintf('"%1$s" has been deprecated in favor of the new "FilterUrlBuilder"', __METHOD__), + \sprintf('"%1$s" has been deprecated in favor of the new "FilterUrlBuilder"', __METHOD__), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd @@ -212,11 +227,16 @@ protected function redirectPost($arrParams) /** * Retrieve the list of parameter names that shall be evaluated. * - * @return array + * @return list */ protected function getWantedNames() { - return (array) unserialize($this->objFilterConfig->metamodel_fef_params); + return \array_values( + \array_map( + static fn (mixed $value): string => (string) $value, + (array) \unserialize($this->objFilterConfig->metamodel_fef_params, ['allowed_classes' => false]) + ) + ); } /** @@ -233,39 +253,38 @@ protected function getParams() { // @codingStandardsIgnoreStart @trigger_error( - sprintf('"%1$s" has been deprecated in favor of the new "FilterUrlBuilder"', __METHOD__), + \sprintf('"%1$s" has been deprecated in favor of the new "FilterUrlBuilder"', __METHOD__), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd $arrWantedParam = $this->getWantedNames(); - $arrMyParams = $arrOtherParams = array(); + $arrMyParams = $arrOtherParams = []; if ($_GET) { - foreach (array_keys($_GET) as $strParam) { - if (in_array($strParam, $arrWantedParam)) { - $arrMyParams[$strParam] = Input::get($strParam); - } elseif ($strParam != 'page') { + foreach (\array_keys($_GET) as $strParam) { + if (\in_array($strParam, $arrWantedParam)) { + $arrMyParams[$strParam] = Input::get((string) $strParam); + } elseif ($strParam !== 'page') { // Add only to the array if param is not page. - $arrOtherParams[$strParam] = Input::get($strParam); + $arrOtherParams[$strParam] = Input::get((string) $strParam); } } } // if POST, translate to proper GET url - if ($_POST && (Input::post('FORM_SUBMIT') == $this->formId)) { - foreach (array_keys($_POST) as $strParam) { - if (in_array($strParam, $arrWantedParam)) { - $arrMyParams[$strParam] = Input::post($strParam); + if ($_POST && (Input::post('FORM_SUBMIT') === $this->formId)) { + foreach (\array_keys($_POST) as $strParam) { + if (\in_array($strParam, $arrWantedParam)) { + $arrMyParams[$strParam] = Input::post((string) $strParam); } } } - return array - ( + return [ 'filter' => $arrMyParams, - 'other' => $arrOtherParams, - 'all' => array_merge($arrOtherParams, $arrMyParams) - ); + 'other' => $arrOtherParams, + 'all' => \array_merge($arrOtherParams, $arrMyParams) + ]; } /** @@ -280,10 +299,11 @@ protected function renderWidget($widget, $filterOptions) { $filter = $widget; $templateName = ($filter['raw']['eval']['template'] ?? 'mm_filteritem_default'); - $template = new \FrontendTemplate($templateName); + $template = new FrontendTemplate($templateName); $template->setData($filter); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->submit = $filterOptions->isAutoSubmit(); $filter['value'] = $template->parse(); @@ -305,7 +325,7 @@ protected function checkRedirect($widgets, $wantedParameter, $allParameter) { // @codingStandardsIgnoreStart @trigger_error( - sprintf('"%1$s" has been deprecated in favor of the new "FilterUrlBuilder"', __METHOD__), + \sprintf('"%1$s" has been deprecated in favor of the new "FilterUrlBuilder"', __METHOD__), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd @@ -337,17 +357,17 @@ protected function checkRedirect($widgets, $wantedParameter, $allParameter) */ protected function getFrontendFilterOptions() { - $objFrontendFilterOptions = new FrontendFilterOptions(); - $objFrontendFilterOptions->setAutoSubmit($this->objFilterConfig->metamodel_fef_autosubmit ? true : false); - $objFrontendFilterOptions->setHideClearFilter( + $filterOptions = new FrontendFilterOptions(); + $filterOptions->setAutoSubmit($this->objFilterConfig->metamodel_fef_autosubmit ? true : false); + $filterOptions->setHideClearFilter( $this->objFilterConfig->metamodel_fef_hideclearfilter ? true : false ); - $objFrontendFilterOptions->setShowCountValues( + $filterOptions->setShowCountValues( $this->objFilterConfig->metamodel_available_values ? true : false ); - $objFrontendFilterOptions->setUrlFragment((string) $this->objFilterConfig->metamodel_fef_urlfragment); + $filterOptions->setUrlFragment($this->objFilterConfig->metamodel_fef_urlfragment); - return $objFrontendFilterOptions; + return $filterOptions; } /** @@ -383,7 +403,7 @@ protected function getFilters() } $arrWidgets = $filterSetting->getParameterFilterWidgets( - array_merge($all->getSlugParameters(), $all->getGetParameters()), + \array_merge($all->getSlugParameters(), $all->getGetParameters()), $jumpToInformation, $filterOptions ); @@ -413,16 +433,17 @@ protected function getFilters() } // Return filter data. + /** @psalm-suppress UndefinedMagicPropertyFetch */ return [ 'action' => $this->filterUrlBuilder->generate($other) . ($this->objFilterConfig->metamodel_fef_urlfragment - ? '#' . $this->objFilterConfig->metamodel_fef_urlfragment - : ''), + ? '#' . $this->objFilterConfig->metamodel_fef_urlfragment + : ''), 'formid' => $this->formId, 'filters' => $renderedWidgets, 'submit' => ($filterOptions->isAutoSubmit() - ? '' - : $GLOBALS['TL_LANG']['metamodels_frontendfilter']['submit'] + ? '' + : $GLOBALS['TL_LANG']['metamodels_frontendfilter']['submit'] ?? '' ) ]; } @@ -430,28 +451,30 @@ protected function getFilters() /** * Retrieve the parameter values. * - * @param FilterUrl $other Destination for "other" parameters (not originating from current filter module). - * @param FilterUrl $all Destination for "all" parameters. - * @param string[] $wantedNames The wanted parameter names. + * @param FilterUrl $other Destination for "other" parameters (not originating from current filter module). + * @param FilterUrl $all Destination for "all" parameters. + * @param list $wantedNames The wanted parameter names. * * @return void */ protected function buildParameters(FilterUrl $other, FilterUrl $all, array $wantedNames): void { - $current = $this->filterUrlBuilder->getCurrentFilterUrl([ - 'postAsSlug' => $wantedNames, - 'postAsGet' => [], - 'preserveGet' => true - ]); + $current = $this->filterUrlBuilder->getCurrentFilterUrl( + [ + 'postAsSlug' => $wantedNames, + 'postAsGet' => [], + 'preserveGet' => true + ] + ); foreach ($current->getSlugParameters() as $name => $value) { $all->setSlug($name, $value); - if (!in_array($name, $wantedNames)) { + if (!\in_array($name, $wantedNames)) { $other->setSlug($name, $value); } } foreach ($current->getGetParameters() as $name => $value) { $all->setGet($name, $value); - if (!in_array($name, $wantedNames)) { + if (!\in_array($name, $wantedNames)) { $other->setGet($name, $value); } } @@ -495,11 +518,11 @@ protected function generateModule($content, $replace, $moduleId) * * @return string * - * @throws \Doctrine\DBAL\DBALException When a database error occur. + * @throws Exception When a database error occur. */ protected function generateElement($table, $content, $replace, $elementId) { - $objDbResult = $this->connection + $result = $this->connection ->createQueryBuilder() ->select('t.*') ->from($table, 't') @@ -507,24 +530,26 @@ protected function generateElement($table, $content, $replace, $elementId) ->andWhere('t.type=:type') ->setParameter('id', $elementId) ->setParameter('type', 'metamodels_frontendclearall') - ->execute() - ->fetch(\PDO::FETCH_OBJ); + ->executeQuery() + ->fetchAssociative(); // Check if we have an existing module or ce element. - if ($objDbResult === false) { - return str_replace($replace, '', $content); + if ($result === false) { + return \str_replace($replace, '', $content); } // Get instance and call generate function. - if ($table == 'tl_module') { - $objElement = new ModuleFilterClearAll($objDbResult); - } elseif ($table == 'tl_content') { - $objElement = new ContentElementFilterClearAll($objDbResult); + if ($table === 'tl_module') { + /** @psalm-suppress ArgumentTypeCoercion */ + $objElement = new ModuleFilterClearAll((object) $result); + } elseif ($table === 'tl_content') { + /** @psalm-suppress ArgumentTypeCoercion */ + $objElement = new ContentElementFilterClearAll((object) $result); } else { - return str_replace($replace, '', $content); + return \str_replace($replace, '', $content); } - return str_replace($replace, $objElement->generateReal(), $content); + return \str_replace($replace, $objElement->generateReal(), $content); } /** @@ -537,29 +562,31 @@ protected function generateElement($table, $content, $replace, $elementId) * * @return string * - * @throws \RuntimeException When an invalid selector has been used (different than "ce" or "mod"). + * @throws RuntimeException When an invalid selector has been used (different than "ce" or "mod"). */ public function generateClearAll($strContent, $strTemplate) { - if (substr($strTemplate, 0, 3) === 'fe_') { - if (preg_match_all( - '#\[\[\[metamodelfrontendfilterclearall::(ce|mod)::([^\]]*)\]\]\]#', - $strContent, - $arrMatches, - PREG_SET_ORDER - )) { + if (\str_starts_with($strTemplate, 'fe_')) { + if ( + (bool) \preg_match_all( + '#\[\[\[metamodelfrontendfilterclearall::(ce|mod)::([^\]]*)\]\]\]#', + $strContent, + $arrMatches, + PREG_SET_ORDER + ) + ) { foreach ($arrMatches as $arrMatch) { switch ($arrMatch[1]) { case 'ce': - $strContent = $this->generateContentElement($strContent, $arrMatch[0], $arrMatch[2]); + $strContent = $this->generateContentElement($strContent, $arrMatch[0], (int) $arrMatch[2]); break; case 'mod': - $strContent = $this->generateModule($strContent, $arrMatch[0], $arrMatch[2]); + $strContent = $this->generateModule($strContent, $arrMatch[0], (int) $arrMatch[2]); break; default: - throw new \RuntimeException('Unexpected element determinator encountered: ' . $arrMatch[1]); + throw new RuntimeException('Unexpected element determinator encountered: ' . $arrMatch[1]); } } } diff --git a/src/FrontendIntegration/HybridFilterBlock.php b/src/FrontendIntegration/HybridFilterBlock.php index 39466707b..fd376f0dd 100644 --- a/src/FrontendIntegration/HybridFilterBlock.php +++ b/src/FrontendIntegration/HybridFilterBlock.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,13 +17,15 @@ * @author David Molineus * @author Andreas Nölke * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\FrontendIntegration; +use Contao\Database\Result; +use Contao\FrontendTemplate; use Contao\System; use MetaModels\Filter\FilterUrlBuilder; use MetaModels\Filter\Setting\ICollection; @@ -32,14 +34,18 @@ /** * FE-module for FE-filtering. * - * @property \FrontendTemplate $Template - * @property string $metamodel_jumpTo - * @property string $metamodel_fef_template - * @property bool $metamodel_fef_autosubmit - * @property bool $metamodel_fef_hideclearfilter - * @property bool $metamodel_available_values - * @property string $metamodel_fef_params - * @property int $id + * @property FrontendTemplate $Template + * @property string $metamodel_jumpTo + * @property string $metamodel_fef_template + * @property bool $metamodel_fef_autosubmit + * @property bool $metamodel_fef_hideclearfilter + * @property bool $metamodel_available_values + * @property string $metamodel_fef_params + * @property string $metamodel_fef_urlfragment + * @property int $id + * + * @psalm-suppress DeprecatedClass + * @psalm-suppress PropertyNotSetInConstructor */ class HybridFilterBlock extends MetaModelHybrid { @@ -53,23 +59,23 @@ class HybridFilterBlock extends MetaModelHybrid /** * The jumpTo page. * - * @var array + * @var array|null */ - private $arrJumpTo; + private ?array $arrJumpTo = null; /** * The database connection. * - * @var IFilterSettingFactory + * @var IFilterSettingFactory|null */ - private $filterFactory; + private ?IFilterSettingFactory $filterFactory = null; /** * The filter URL builder. * - * @var FilterUrlBuilder + * @var FilterUrlBuilder|null */ - private $filterUrlBuilder; + private ?FilterUrlBuilder $filterUrlBuilder = null; /** * Get the jump to page data. @@ -81,26 +87,28 @@ class HybridFilterBlock extends MetaModelHybrid */ public function getJumpTo() { - if (!isset($this->arrJumpTo)) { - /** @var \Database\Result $page */ - $page = $GLOBALS['objPage']; - $this->setJumpTo($page->row()); - + if (null === $this->arrJumpTo) { if ($this->metamodel_jumpTo) { // Page to jump to when filter submit. $statement = $this->getConnection() ->createQueryBuilder() - ->select('t.id, t.alias') + ->select('t.id', 't.alias') ->from('tl_page', 't') ->where('t.id=:id') ->setParameter('id', $this->metamodel_jumpTo) ->setMaxResults(1) - ->execute(); + ->executeQuery(); + + if (false !== ($row = $statement->fetchAssociative())) { + $this->arrJumpTo = $row; - if ($statement->rowCount()) { - $this->setJumpTo($statement->fetch(\PDO::FETCH_ASSOC)); + return $this->arrJumpTo; } } + + /** @var Result $page */ + $page = $GLOBALS['objPage']; + $this->arrJumpTo = $page->row(); } return $this->arrJumpTo; @@ -127,6 +135,7 @@ public function setJumpTo($arrJumpTo) */ public function getFilterCollection() { + /** @psalm-suppress UndefinedThisPropertyFetch */ return $this ->getFilterFactory() ->createCollection($this->metamodel_filtering); @@ -144,6 +153,7 @@ public function generate() $this->strTemplate = $this->metamodel_fef_template; } + /** @psalm-suppress DeprecatedClass */ return parent::generate(); } @@ -157,7 +167,8 @@ protected function compile() $objFilter = new FrontendFilter($this->getConnection(), $this->getFilterUrlBuilder()); $arrFilter = $objFilter->getMetaModelFrontendFilter($this); - $this->Template->setData(array_merge($this->Template->getData(), $arrFilter)); + $this->Template->setData(\array_merge($this->Template->getData(), $arrFilter)); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $this->Template->submit = $arrFilter['submit']; } @@ -169,7 +180,10 @@ protected function compile() private function getFilterFactory(): IFilterSettingFactory { if (null === $this->filterFactory) { - return $this->filterFactory = System::getContainer()->get('metamodels.filter_setting_factory'); + $filterSettingFactory = System::getContainer()->get('metamodels.filter_setting_factory'); + assert($filterSettingFactory instanceof IFilterSettingFactory); + + return $this->filterFactory = $filterSettingFactory; } return $this->filterFactory; @@ -183,7 +197,10 @@ private function getFilterFactory(): IFilterSettingFactory private function getFilterUrlBuilder(): FilterUrlBuilder { if (null === $this->filterUrlBuilder) { - return $this->filterUrlBuilder = System::getContainer()->get('metamodels.filter_url'); + $filterUrl = System::getContainer()->get('metamodels.filter_url'); + assert($filterUrl instanceof FilterUrlBuilder); + + return $this->filterUrlBuilder = $filterUrl; } return $this->filterUrlBuilder; diff --git a/src/FrontendIntegration/HybridFilterClearAll.php b/src/FrontendIntegration/HybridFilterClearAll.php index 1d18b42d7..305963697 100644 --- a/src/FrontendIntegration/HybridFilterClearAll.php +++ b/src/FrontendIntegration/HybridFilterClearAll.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,19 +16,24 @@ * @author Sven Baumann * @author Andreas Nölke * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\FrontendIntegration; +use Contao\FrontendTemplate; use Contao\System; +use MetaModels\Filter\FilterUrlBuilder; +use Symfony\Component\HttpFoundation\Request; /** * Content element clearing the FE-filter. * - * @property \FrontendTemplate $Template + * @property FrontendTemplate $Template + * + * @psalm-suppress DeprecatedClass */ abstract class HybridFilterClearAll extends MetaModelHybrid { @@ -60,11 +65,17 @@ abstract class HybridFilterClearAll extends MetaModelHybrid */ public function generate() { - if (TL_MODE == 'BE') { + if ( + (bool) System::getContainer()->get('contao.routing.scope_matcher') + ?->isBackendRequest( + System::getContainer()->get('request_stack')?->getCurrentRequest() ?? Request::create('') + ) + ) { + /** @psalm-suppress DeprecatedClass */ return parent::generate(); } - return sprintf('[[[metamodelfrontendfilterclearall::%s::%s]]]', $this->type, $this->id); + return \sprintf('[[[metamodelfrontendfilterclearall::%s::%s]]]', $this->type, $this->id); } /** @@ -79,7 +90,10 @@ protected function compile() { $blnActiveParam = false; $filterUrlBuilder = System::getContainer()->get('metamodels.filter_url'); - $filterUrl = $filterUrlBuilder->getCurrentFilterUrl(); + assert($filterUrlBuilder instanceof FilterUrlBuilder); + + $filterUrl = $filterUrlBuilder->getCurrentFilterUrl(); + foreach ($GLOBALS['MM_FILTER_PARAMS'] as $param) { if ($filterUrl->hasSlug($param)) { $filterUrl->setSlug($param, ''); @@ -94,10 +108,12 @@ protected function compile() } // Check if we have filter and if we have active params. + /** @psalm-suppress InvalidPropertyAssignmentValue */ $this->Template->active = ( - !is_array($GLOBALS['MM_FILTER_PARAMS']) - || count($GLOBALS['MM_FILTER_PARAMS']) == 0 + !\is_array($GLOBALS['MM_FILTER_PARAMS']) + || \count($GLOBALS['MM_FILTER_PARAMS']) === 0 ); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $this->Template->activeParam = $blnActiveParam; // Build FE url. @@ -116,6 +132,7 @@ public function generateReal() $this->strTemplate = $this->metamodel_fef_template; } + /** @psalm-suppress DeprecatedClass */ return parent::generate(); } } diff --git a/src/FrontendIntegration/HybridList.php b/src/FrontendIntegration/HybridList.php index d174e851b..3b194332f 100644 --- a/src/FrontendIntegration/HybridList.php +++ b/src/FrontendIntegration/HybridList.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,22 +19,29 @@ * @author Oliver Hoff * @author Stefan Heimes * @author Richard Henkenjohann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\FrontendIntegration; +use Contao\FrontendTemplate; use Contao\Input; use Contao\StringUtil; use Contao\System; +use MetaModels\Filter\FilterUrlBuilder; use MetaModels\ItemList; /** * Implementation of the MetaModel content element. * - * @property \FrontendTemplate $Template + * @property FrontendTemplate $Template + * + * @deprecated We switched to fragments {@see ItemListController.php} in MetaModels 2.2. To be removed in MetaModels 3. + * + * @psalm-suppress DeprecatedClass + * @psalm-suppress PropertyNotSetInConstructor */ class HybridList extends MetaModelHybrid { @@ -57,6 +64,7 @@ public function generate() $this->strTemplate = $this->metamodel_layout; } + /** @psalm-suppress DeprecatedClass */ return parent::generate(); } @@ -66,19 +74,25 @@ public function generate() * * @param ItemList $objItemRenderer The list renderer instance to be used. * - * @return string[] + * @return array> */ protected function getFilterParameters($objItemRenderer) { $filterUrlBuilder = System::getContainer()->get('metamodels.filter_url'); - $filterUrl = $filterUrlBuilder->getCurrentFilterUrl(); + assert($filterUrlBuilder instanceof FilterUrlBuilder); + + $filterUrl = $filterUrlBuilder->getCurrentFilterUrl(); $result = []; foreach ($objItemRenderer->getFilterSettings()->getParameters() as $name) { if ($filterUrl->hasSlug($name)) { - $result[$name] = $filterUrl->getSlug($name); + $value = $filterUrl->getSlug($name); + assert(\is_string($value)); + $result[$name] = $value; } elseif ($filterUrl->hasGet($name)) { - $result[$name] = $filterUrl->getGet($name); + $value = $filterUrl->getGet($name); + assert(\is_array($value) || \is_string($value)); + $result[$name] = $value; } // DAMN Contao - we have to "mark" the keys in the Input class as used as we get an 404 otherwise. Input::get($name); @@ -96,19 +110,29 @@ protected function compile() { $objItemRenderer = new ItemList(); + /** + * @psalm-suppress UndefinedThisPropertyFetch + * @psalm-suppress UndefinedMagicPropertyAssignment + */ $this->Template->searchable = !$this->metamodel_donotindex; - $sorting = $this->metamodel_sortby; + /** @psalm-suppress UndefinedThisPropertyFetch */ + $sorting = $this->metamodel_sortby; + /** @psalm-suppress UndefinedThisPropertyFetch */ $direction = $this->metamodel_sortby_direction; if ($this->metamodel_sort_override) { - if (\Input::get('orderBy')) { - $sorting = \Input::get('orderBy'); + if (Input::get('orderBy')) { + $sorting = Input::get('orderBy'); } - if (\Input::get('orderDir')) { - $direction = \Input::get('orderDir'); + if (Input::get('orderDir')) { + $direction = Input::get('orderDir'); } } + /** + * @psalm-suppress UndefinedThisPropertyFetch + * @psalm-suppress UndefinedMagicPropertyAssignment + */ $objItemRenderer ->setMetaModel($this->metamodel, $this->metamodel_rendersettings) ->setLimit($this->metamodel_use_limit, $this->metamodel_offset, $this->metamodel_limit) @@ -122,9 +146,15 @@ protected function compile() ->setMetaTags($this->metamodel_meta_title, $this->metamodel_meta_description); // Render items with encoded email strings as contao standard. + /** + * @psalm-suppress UndefinedThisPropertyFetch + * @psalm-suppress UndefinedMagicPropertyAssignment + */ $this->Template->items = - \StringUtil::encodeEmail($objItemRenderer->render($this->metamodel_noparsing, $this)); + StringUtil::encodeEmail($objItemRenderer->render($this->metamodel_noparsing, $this)); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $this->Template->numberOfItems = $objItemRenderer->getItems()->getCount(); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $this->Template->pagination = $objItemRenderer->getPagination(); } } diff --git a/src/FrontendIntegration/MetaModelHybrid.php b/src/FrontendIntegration/MetaModelHybrid.php index 40f23f790..13c3477dc 100644 --- a/src/FrontendIntegration/MetaModelHybrid.php +++ b/src/FrontendIntegration/MetaModelHybrid.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -16,7 +16,7 @@ * @author Ingolf Steinhardt * @author David Molineus * @author Richard Henkenjohann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,25 +24,38 @@ namespace MetaModels\FrontendIntegration; use Contao\BackendTemplate; -use Contao\ContentModel; -use Contao\FormModel; +use Contao\Database\Result; use Contao\Hybrid; -use Contao\ModuleModel; use Contao\StringUtil; use Contao\System; use Doctrine\DBAL\Connection; use MetaModels\IFactory; +use MetaModels\IMetaModel; use MetaModels\IMetaModelsServiceContainer; use MetaModels\MetaModelsServiceContainer; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Contracts\Translation\TranslatorInterface; /** * Base implementation of a MetaModel Hybrid element. * - * @property string metamodel The id of the MetaModel to use. - * @property string metamodel_filtering The id of the MetaModel filter setting to use. - * @property string metamodel_rendersettings The id of the MetaModel render setting to use. + * @property string $id The id of the element. + * @property string $name The module name to use (if type is module). + * @property string $metamodel The id of the MetaModel to use. + * @property string $metamodel_filtering The id of the MetaModel filter setting to use. + * @property string $metamodel_rendersettings The id of the MetaModel render setting to use. + * @property bool $metamodel_sort_override The flag to override sorting. + * + * @psalm-type TDatabaseResult=object{ + * cssID: string, + * typePrefix: ?string, + * type: string, + * headline: string, + * } * * @deprecated We switched to fragments in MetaModels 2.2. To be removed in MetaModels 3.0. + * + * @psalm-suppress PropertyNotSetInConstructor */ abstract class MetaModelHybrid extends Hybrid { @@ -70,9 +83,9 @@ abstract class MetaModelHybrid extends Hybrid /** * The database connection. * - * @var Connection + * @var Connection|null */ - private $connection; + private ?Connection $connection = null; /** * Retrieve the service container. @@ -80,6 +93,8 @@ abstract class MetaModelHybrid extends Hybrid * @return IMetaModelsServiceContainer * * @deprecated The service container will get removed, inject needed services instead. + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer() { @@ -90,7 +105,11 @@ public function getServiceContainer() ); // @codingStandardsIgnoreEnd - return System::getContainer()->get(MetaModelsServiceContainer::class); + /** @psalm-suppress DeprecatedClass */ + $serviceContainer = System::getContainer()->get(MetaModelsServiceContainer::class); + assert($serviceContainer instanceof MetaModelsServiceContainer); + + return $serviceContainer; } /** @@ -100,7 +119,10 @@ public function getServiceContainer() */ protected function getFactory() { - return System::getContainer()->get('metamodels.factory'); + $factory = System::getContainer()->get('metamodels.factory'); + assert($factory instanceof IFactory); + + return $factory; } /** @@ -113,12 +135,13 @@ protected function getConnection() if (null === $this->connection) { // @codingStandardsIgnoreStart @trigger_error( - 'Connection is missing in class ' . static::class . - '. The automatic fallback will be dropped in MetaModels 3.0. Please use dependency injection', + 'Connection is missing. It has to be passed in the constructor. Fallback will be dropped.', E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - return $this->connection = System::getContainer()->get('database_connection'); + $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); + $this->connection = $connection; } return $this->connection; @@ -127,23 +150,26 @@ protected function getConnection() /** * Create a new instance. * - * @param ContentModel|ModuleModel|FormModel $objElement The object from the database. - * + * @param Result|TDatabaseResult $objElement The object from the database. * @param string $strColumn The column the element is displayed within. */ public function __construct($objElement, $strColumn = 'main') { + /** @psalm-suppress ArgumentTypeCoercion - Contao has incomplete type annotation. */ parent::__construct($objElement, $strColumn); - $this->arrData = method_exists($objElement, 'row') ? $objElement->row() : (array) $objElement; + $this->arrData = \method_exists($objElement, 'row') ? $objElement->row() : (array) $objElement; // Get CSS ID and headline from the parent element (!). + /** @psalm-suppress UndefinedThisPropertyFetch */ $this->cssID = StringUtil::deserialize($objElement->cssID, true); - $this->typePrefix = $objElement->typePrefix; - $this->strKey = $objElement->type; - $arrHeadline = StringUtil::deserialize($objElement->headline); - $this->headline = is_array($arrHeadline) ? $arrHeadline['value'] : $arrHeadline; - $this->hl = is_array($arrHeadline) ? $arrHeadline['unit'] : 'h1'; + $this->typePrefix = $objElement->typePrefix ?? ''; + /** @psalm-suppress UndefinedThisPropertyFetch */ + $this->strKey = $objElement->type; + $arrHeadline = StringUtil::deserialize($objElement->headline); + /** @psalm-suppress UndefinedThisPropertyFetch */ + $this->headline = \is_array($arrHeadline) ? $arrHeadline['value'] : $arrHeadline; + $this->hl = \is_array($arrHeadline) ? $arrHeadline['unit'] : 'h1'; } /** @@ -153,28 +179,37 @@ public function __construct($objElement, $strColumn = 'main') * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) + * @throws \Doctrine\DBAL\Exception */ public function generate() { - if (TL_MODE == 'BE') { + if ( + (bool) System::getContainer()->get('contao.routing.scope_matcher') + ?->isBackendRequest( + System::getContainer()->get('request_stack')?->getCurrentRequest() ?? Request::create('') + ) + ) { $strInfo = ''; if ($this->metamodel) { // Add CSS file. $GLOBALS['TL_CSS'][] = 'bundles/metamodelscore/css/style.css'; - // Retrieve name of MetaModels. + // Retrieve name of MetaModel. $infoTemplate = '
    %s: %s
    '; $factory = $this->getFactory(); - if (null === $metaModelName = $factory->translateIdToMetaModelName($this->metamodel)) { - return 'Unknown MetaModel: ' . $this->metamodel; - } + $metaModelName = $factory->translateIdToMetaModelName($this->metamodel); $metaModel = $factory->getMetaModel($metaModelName); - $strInfo = sprintf( + assert($metaModel instanceof IMetaModel); + + $translator = System::getContainer()->get('translator'); + assert($translator instanceof TranslatorInterface); + + $strInfo = \sprintf( $infoTemplate, - $GLOBALS['TL_LANG']['MSC']['mm_be_info_name'][1], - $GLOBALS['TL_LANG']['MSC']['mm_be_info_name'][0], + $translator->trans('mm_be_info_name.description', [], 'metamodels_wildcard'), + $translator->trans('mm_be_info_name.label', [], 'metamodels_wildcard'), $metaModel->getName() ); @@ -184,51 +219,57 @@ public function generate() if ($this->metamodel_filtering) { $infoFi = $database ->createQueryBuilder() - ->select('name') - ->from('tl_metamodel_filter') - ->where('id=:id') + ->select('t.name') + ->from('tl_metamodel_filter', 't') + ->where('t.id=:id') ->setParameter('id', $this->metamodel_filtering) - ->execute() - ->fetch(\PDO::FETCH_COLUMN); + ->setMaxResults(1) + ->executeQuery() + ->fetchFirstColumn(); if ($infoFi) { - $strInfo .= sprintf( + $strInfo .= \sprintf( $infoTemplate, - $GLOBALS['TL_LANG']['MSC']['mm_be_info_filter'][1], - $GLOBALS['TL_LANG']['MSC']['mm_be_info_filter'][0], - $infoFi + $translator->trans('mm_be_info_filter.description', [], 'metamodels_wildcard'), + $translator->trans('mm_be_info_filter.label', [], 'metamodels_wildcard'), + \current($infoFi) ); } } - // Retrieve name of rendersetting. + // Retrieve name of render setting. if ($this->metamodel_rendersettings) { $infoRs = $database ->createQueryBuilder() - ->select('name') - ->from('tl_metamodel_rendersettings') - ->where('id=:id') + ->select('t.name') + ->from('tl_metamodel_rendersettings', 't') + ->where('t.id=:id') ->setParameter('id', $this->metamodel_rendersettings) - ->execute() - ->fetch(\PDO::FETCH_COLUMN); + ->setMaxResults(1) + ->executeQuery() + ->fetchFirstColumn(); if ($infoRs) { - $strInfo .= sprintf( + $strInfo .= \sprintf( $infoTemplate, - $GLOBALS['TL_LANG']['MSC']['mm_be_info_render_setting'][1], - $GLOBALS['TL_LANG']['MSC']['mm_be_info_render_setting'][0], - $infoRs + $translator->trans('mm_be_info_render_setting.description', [], 'metamodels_wildcard'), + $translator->trans('mm_be_info_render_setting.label', [], 'metamodels_wildcard'), + \current($infoRs) ); } } } - $objTemplate = new BackendTemplate('be_wildcard'); + $objTemplate = new BackendTemplate('be_wildcard'); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $objTemplate->wildcard = $this->wildCardName . $strInfo; - $objTemplate->title = $this->headline; - $objTemplate->id = $this->id; - $objTemplate->link = ($this->typePrefix == 'mod_' ? 'FE-Modul: ' : '') . $this->name; - $objTemplate->href = sprintf($this->wildCardLink, $this->id); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $objTemplate->title = $this->headline; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $objTemplate->id = $this->id; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $objTemplate->link = ($this->typePrefix === 'mod_' ? 'FE-Modul: ' : '') . $this->name; + $objTemplate->href = \sprintf($this->wildCardLink, $this->id); return $objTemplate->parse(); } diff --git a/src/FrontendIntegration/Module/Filter.php b/src/FrontendIntegration/Module/Filter.php index 887f776c9..72973ae09 100644 --- a/src/FrontendIntegration/Module/Filter.php +++ b/src/FrontendIntegration/Module/Filter.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. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -25,6 +25,9 @@ /** * Frontend module for FE-filtering. + * + * @psalm-suppress DeprecatedClass + * @psalm-suppress PropertyNotSetInConstructor */ class Filter extends HybridFilterBlock { diff --git a/src/FrontendIntegration/Module/FilterClearAll.php b/src/FrontendIntegration/Module/FilterClearAll.php index 3a8836a8e..8a02e9e71 100644 --- a/src/FrontendIntegration/Module/FilterClearAll.php +++ b/src/FrontendIntegration/Module/FilterClearAll.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 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. @@ -15,7 +15,7 @@ * @author Stefan Heimes * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +26,9 @@ /** * Content element clearing the FE-filter. + * + * @psalm-suppress DeprecatedClass + * @psalm-suppress PropertyNotSetInConstructor */ class FilterClearAll extends HybridFilterClearAll { diff --git a/src/FrontendIntegration/Module/ModelList.php b/src/FrontendIntegration/Module/ModelList.php index 1584411dc..ff5c78ba0 100644 --- a/src/FrontendIntegration/Module/ModelList.php +++ b/src/FrontendIntegration/Module/ModelList.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. @@ -18,7 +18,7 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author Richard Henkenjohann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,6 +31,9 @@ * Implementation of the MetaModel content element. * * @deprecated We switched to fragments {@see ItemListController.php} in MetaModels 2.2. To be removed in MetaModels 3. + * + * @psalm-suppress DeprecatedClass + * @psalm-suppress PropertyNotSetInConstructor */ class ModelList extends HybridList { diff --git a/src/FrontendIntegration/ViewCombinations.php b/src/FrontendIntegration/ViewCombinations.php index 4a70a6803..caa641b20 100644 --- a/src/FrontendIntegration/ViewCombinations.php +++ b/src/FrontendIntegration/ViewCombinations.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -26,6 +27,8 @@ * Retrieve combinations of view and input screens for the currently logged in user (either frontend or backend). * * @deprecated This will get removed. + * + * @psalm-suppress DeprecatedClass */ class ViewCombinations extends \MetaModels\Helper\ViewCombinations { @@ -36,6 +39,7 @@ class ViewCombinations extends \MetaModels\Helper\ViewCombinations */ protected function authenticateUser() { + /** @psalm-suppress DeprecatedMethod */ return $this->getUser()->authenticate(); } @@ -44,12 +48,12 @@ protected function authenticateUser() */ protected function getUserGroups() { - /** @noinspection PhpUndefinedFieldInspection */ // Special case in combinations, anonymous frontend users have the implicit group id -1. - if (!$this->getUser()->id) { + if (0 === (int) $this->getUser()->id) { return [-1]; } + /** psalm-suppress DeprecatedClass */ return parent::getUserGroups(); } } diff --git a/src/Helper/ContaoController.php b/src/Helper/ContaoController.php index 5f0b53fdc..a769de9cb 100644 --- a/src/Helper/ContaoController.php +++ b/src/Helper/ContaoController.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Maack * @author Sven Baumann - * @copyright 2012-2021 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 */ @@ -38,7 +39,7 @@ class ContaoController extends Controller /** * The instance. * - * @var ContaoController + * @var ContaoController|null */ protected static $objInstance = null; diff --git a/src/Helper/EmptyTest.php b/src/Helper/EmptyTest.php index 17ddd56f4..f46be63b0 100644 --- a/src/Helper/EmptyTest.php +++ b/src/Helper/EmptyTest.php @@ -35,9 +35,11 @@ public static function isEmptyValue($mixValue): bool { if (is_array($mixValue)) { return self::isArrayEmpty($mixValue); - } elseif ('' === $mixValue) { + } + if ('' === $mixValue) { return true; - } elseif (null === $mixValue) { + } + if (null === $mixValue) { return true; } @@ -54,7 +56,7 @@ public static function isEmptyValue($mixValue): bool public static function isArrayEmpty(array $array): bool { // First off check for simple types. - if (empty($array)) { + if ([] === $array) { return true; } // Next check for a value array. @@ -62,15 +64,12 @@ public static function isArrayEmpty(array $array): bool return self::isArrayEmpty($array['value']); } // Now check sub arrays. - if (is_array($array)) { - foreach ($array as $value) { - if (!self::isEmptyValue($value)) { - return false; - } + foreach ($array as $value) { + if (!self::isEmptyValue($value)) { + return false; } - return true; } - return false; + return true; } } diff --git a/src/Helper/LocaleUtil.php b/src/Helper/LocaleUtil.php new file mode 100644 index 000000000..e602d2b5e --- /dev/null +++ b/src/Helper/LocaleUtil.php @@ -0,0 +1,63 @@ + + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\Helper; + +use Contao\CoreBundle\Util\LocaleUtil as ContaoLocaleUtil; +use Contao\System; + +final class LocaleUtil +{ + /** + * Converts a Locale ID to a Language Tag and strips keywords + * after the @ sign. + * As legacy part we convert a Locale ID (_) to a Language Tag (-) + * and strips keywords after the @ sign. + * + * @param string $localeId The locale id. + * + * @return string + */ + public static function formatAsLanguageTag(string $localeId): string + { + $packages = System::getContainer()->getParameter('kernel.packages'); + assert(is_array($packages)); + $coreVersion = $packages['contao/core-bundle'] ?? ''; + + if (\version_compare($coreVersion, '4.13', '>=')) { + return self::formatAsLocale($localeId); + } + + // Legacy call. + return \str_replace('_', '-', ContaoLocaleUtil::formatAsLocale($localeId)); + } + + /** + * Converts a Language Tag (-) to a Locale ID (_) and strips keywords + * after the @ sign. + * + * @param string $languageTag The language tag. + * + * @return string + */ + public static function formatAsLocale(string $languageTag): string + { + return ContaoLocaleUtil::formatAsLocale($languageTag); + } +} diff --git a/src/Helper/PaginationGenerator.php b/src/Helper/PaginationGenerator.php index eaa5b5662..4af8c0299 100644 --- a/src/Helper/PaginationGenerator.php +++ b/src/Helper/PaginationGenerator.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package MetaModels/core * @author Ingolf Steinhardt * @author Christian Schiffler - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -119,7 +119,7 @@ public function __construct( $this->urlBuilder = $urlBuilder; $this->numRows = $numRows; $this->rowsPerPage = $rowsPerPage; - $this->totalPages = ceil($this->numRows / $this->rowsPerPage); + $this->totalPages = (int) \ceil($this->numRows / $this->rowsPerPage); $this->numberOfLinks = $numberOfLinks; $this->pageParam = $pageParam; $this->paramType = $paramType; @@ -153,19 +153,31 @@ public function generateForFilterUrl(FilterUrl $filterUrl): string $page = $this->totalPages; } - $template = $this->template; - $template->hasFirst = $this->hasFirst($page); - $template->hasPrevious = $this->hasPrevious($page); - $template->hasNext = $this->hasNext($page); - $template->hasLast = $this->hasLast($page); - $template->pages = $this->getItemsAsArray($filterUrl, $page); - $template->page = $page; - $template->totalPages = $this->totalPages; - $template->first = $template->hasFirst ? $this->linkToPage($filterUrl, 1) : ''; - $template->previous = $template->hasPrevious ? $this->linkToPage($filterUrl, ($page - 1)) : ''; - $template->next = $template->hasNext ? $this->linkToPage($filterUrl, ($page + 1)) : ''; - $template->last = $template->hasLast ? $this->linkToPage($filterUrl, $this->totalPages) : ''; - $template->class = 'pagination-' . $this->pageParam; + $template = $this->template; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->hasFirst = $this->hasFirst($page); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->hasPrevious = $this->hasPrevious($page); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->hasNext = $this->hasNext($page); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->hasLast = $this->hasLast($page); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->pages = $this->getItemsAsArray($filterUrl, $page); + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->page = $page; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->totalPages = $this->totalPages; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->first = $template->hasFirst ? $this->linkToPage($filterUrl, 1) : ''; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->previous = $template->hasPrevious ? $this->linkToPage($filterUrl, ($page - 1)) : ''; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->next = $template->hasNext ? $this->linkToPage($filterUrl, ($page + 1)) : ''; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ + $template->last = $template->hasLast ? $this->linkToPage($filterUrl, $this->totalPages) : ''; + $template->class = 'pagination-' . $this->pageParam; + /** @psalm-suppress UndefinedMagicPropertyAssignment */ $template->paginationFragment = $this->paginationFragment; // @codingStandardsIgnoreStart // Adding rel="prev" and rel="next" links is not possible @@ -188,19 +200,34 @@ private function getCurrentPage(FilterUrl $filterUrl): int { switch ($this->paramType) { case 'get': - return (int) ($filterUrl->getGet($this->pageParam) ?? 1); + return (int) ($this->getGetPageParam($filterUrl) ?? 1); case 'slug': return (int) ($filterUrl->getSlug($this->pageParam) ?? 1); case 'slugNget': - return (int) ($filterUrl->getGet($this->pageParam) - ?? $filterUrl->getSlug($this->pageParam) - ?? 1); + return (int) ($this->getGetPageParam($filterUrl) ?? $filterUrl->getSlug($this->pageParam) ?? 1); default: } throw new InvalidArgumentException('Invalid configured value: ' . $this->paramType); } + /** + * Retrieve GET parameters. + * + * @param FilterUrl $filterUrl The filter URL. + * + * @return string|null + */ + private function getGetPageParam(FilterUrl $filterUrl): ?string + { + $value = $filterUrl->getGet($this->pageParam); + if (\is_array($value)) { + return null; + } + + return $value; + } + /** * Return true if the pagination menu has a "<< first" link * @@ -261,7 +288,7 @@ private function getItemsAsArray(FilterUrl $filterUrl, int $page): array { $links = []; - $numberOfLinks = floor($this->numberOfLinks / 2); + $numberOfLinks = (int) floor($this->numberOfLinks / 2); $firstOffset = ($page - $numberOfLinks - 1); if ($firstOffset > 0) { @@ -287,7 +314,7 @@ private function getItemsAsArray(FilterUrl $filterUrl, int $page): array } for ($i = $firstLink; $i <= $lastLink; $i++) { - if ($i == $page) { + if ($i === $page) { $links[] = [ 'page' => $i, 'href' => null @@ -320,9 +347,9 @@ private function linkToPage(FilterUrl $filterUrl, int $page): string $pageFilterUrl = $filterUrl->clone(); if ($this->paramType === 'get') { - $pageFilterUrl->setGet($this->pageParam, $page); + $pageFilterUrl->setGet($this->pageParam, (string) $page); } else { - $pageFilterUrl->setSlug($this->pageParam, $page); + $pageFilterUrl->setSlug($this->pageParam, (string) $page)->setGet($this->pageParam, ''); } return $this->urlBuilder->generate($pageFilterUrl); diff --git a/src/Helper/PaginationLimitCalculator.php b/src/Helper/PaginationLimitCalculator.php index 0166bc5f3..14d033bc9 100644 --- a/src/Helper/PaginationLimitCalculator.php +++ b/src/Helper/PaginationLimitCalculator.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Richard Henkenjohann * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -31,6 +31,8 @@ /** * Helper class to calculate limit and offset and pagination. + * + * @SuppressWarnings(PHPMD.TooManyFields) */ class PaginationLimitCalculator { @@ -60,7 +62,7 @@ class PaginationLimitCalculator * * @var int */ - private int $currentPage; + private int $currentPage = 0; /** * Pagination page break. @@ -102,7 +104,7 @@ class PaginationLimitCalculator * * @var int */ - private int $calculatedTotal; + private int $calculatedTotal = 0; /** * Flag if the data needs to be recalculated. @@ -114,7 +116,7 @@ class PaginationLimitCalculator /** * The filter url builder. * - * @var FilterUrlBuilder|object|null + * @var FilterUrlBuilder */ private FilterUrlBuilder $filterUrlBuilder; @@ -146,6 +148,13 @@ class PaginationLimitCalculator */ private string $paginationTemplate; + /** + * The filter URL. + * + * @var FilterUrl + */ + private FilterUrl $filterUrl; + /** * Create a new instance. * @@ -170,14 +179,14 @@ public function __construct( $this->paginationTemplate = $paginationTemplate; $this->paginationFragment = $paginationFragment; if (null === $filterUrlBuilder) { - $filterUrlBuilder = System::getContainer()->get('metamodels.filter_url'); // @codingStandardsIgnoreStart - @trigger_deprecation( - 'metamodels/core', - '2.2.0', - __CLASS__ . ' parameter FilterUrlBuilder is null, but the parameter should be set' + @trigger_error( + 'FilterUrlBuilder is missing. It has to be passed in the constructor. Fallback will be dropped.', + E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + $filterUrlBuilder = System::getContainer()->get('metamodels.filter_url'); + assert($filterUrlBuilder instanceof FilterUrlBuilder); } $this->filterUrlBuilder = $filterUrlBuilder; @@ -288,19 +297,17 @@ public function setLimit(int $limit): self */ public function getCurrentPage(): int { - if (isset($this->currentPage)) { + if (0 !== $this->currentPage) { return $this->currentPage; } switch ($this->paramType) { case 'get': - return (int) ($this->filterUrl->getGet($this->pageParam) ?? 1); + return (int) ($this->getGetPageParam() ?? 1); case 'slug': return (int) ($this->filterUrl->getSlug($this->pageParam) ?? 1); case 'slugNget': - return (int) ($this->filterUrl->getGet($this->pageParam) - ?? $this->filterUrl->getSlug($this->pageParam) - ?? 1); + return (int) ($this->getGetPageParam() ?? $this->filterUrl->getSlug($this->pageParam) ?? 1); default: } @@ -321,10 +328,9 @@ public function setCurrentPage(int $currentPage): self $this->currentPage = $currentPage; // @codingStandardsIgnoreStart - @trigger_deprecation( - 'metamodels/core', - '2.2.0', - __METHOD__ . ' is deprecated - the page is determined automatically from the current request' + @trigger_error( + '"' .__METHOD__ . '" is deprecated - the page is determined automatically from the current request.', + E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd @@ -416,7 +422,7 @@ public function getPaginationString(): string { $this->calculate(); - if ($this->getPerPage() == 0) { + if ($this->getPerPage() === 0) { return ''; } @@ -440,7 +446,7 @@ public function getPaginationString(): string /** * Retrieve the calculated offset. * - * @return int + * @return int|null */ public function getCalculatedOffset(): ?int { @@ -452,7 +458,7 @@ public function getCalculatedOffset(): ?int /** * Retrieve the calculated limit. * - * @return int + * @return int|null */ public function getCalculatedLimit(): ?int { @@ -461,42 +467,14 @@ public function getCalculatedLimit(): ?int return $this->calculatedLimit; } - /** - * Calculate the limit and offset with pagination. - * - * @return void - */ - private function calculatePaginated() - { - $this->calculatedTotal = $this->getTotalAmount(); - - // If a total limit has been defined, we need to honor that. - if (($this->calculatedLimit !== null) && ($this->calculatedTotal > $this->calculatedLimit)) { - $this->calculatedTotal -= $this->calculatedLimit; - } - $this->calculatedTotal -= $this->calculatedOffset; - - // Get the current page. - $page = $this->getCurrentPage(); - - if ($page > ($this->calculatedTotal / $this->getPerPage())) { - $page = (int) ceil($this->calculatedTotal / $this->getPerPage()); - } - - // Set limit and offset. - $pageOffset = ((max($page, 1) - 1) * $this->getPerPage()); - $this->calculatedOffset += $pageOffset; - if ($this->calculatedLimit === null) { - $this->calculatedLimit = $this->getPerPage(); - } else { - $this->calculatedLimit = min(($this->calculatedLimit - $this->calculatedOffset), $this->getPerPage()); - } - } - /** * Calculate the pagination based upon the offset, limit and total amount of items. * * @return void + * + * @psalm-assert false $this->isDirty + * @psalm-assert int $this->calculatedOffset + * @psalm-assert int $this->calculatedLimit */ protected function calculate() { @@ -511,11 +489,11 @@ protected function calculate() // If defined, we override the pagination here. if ($this->isLimited()) { - if ($this->getLimit()) { - $this->calculatedLimit = $this->getLimit(); + if ($limit = $this->getLimit()) { + $this->calculatedLimit = $limit; } - if ($this->getOffset()) { - $this->calculatedOffset = $this->getOffset(); + if ($offset = $this->getOffset()) { + $this->calculatedOffset = $offset; } } @@ -532,4 +510,53 @@ protected function calculate() $this->calculatedOffset = 0; } } + + /** + * Calculate the limit and offset with pagination. + * + * @return void + */ + private function calculatePaginated(): void + { + $this->calculatedTotal = $this->getTotalAmount(); + + // If a total limit has been defined, we need to honor that. + if ($this->calculatedOffset !== null) { + $this->calculatedTotal -= $this->calculatedOffset; + } + if (($this->calculatedLimit !== null) && ($this->calculatedTotal > $this->calculatedLimit)) { + $this->calculatedTotal = $this->calculatedLimit; + } + + // Get the current page. + $page = $this->getCurrentPage(); + $perPage = $this->getPerPage(); + + if ($page > ($this->calculatedTotal / $perPage)) { + $page = (int) \ceil($this->calculatedTotal / $perPage); + } + + // Set offset and limit. + $this->calculatedOffset = ($this->calculatedOffset ?? 0) + ((\max($page, 1) - 1) * $perPage); + if (null === $this->calculatedLimit) { + $this->calculatedLimit = $perPage; + } else { + $this->calculatedLimit = \min($this->calculatedTotal, $perPage); + } + } + + /** + * Retrieve GET parameters. + * + * @return string|null + */ + private function getGetPageParam(): ?string + { + $value = $this->filterUrl->getGet($this->pageParam); + if (\is_array($value)) { + return null; + } + + return $value; + } } diff --git a/src/Helper/SortingLinkGenerator.php b/src/Helper/SortingLinkGenerator.php new file mode 100644 index 000000000..d9c42e379 --- /dev/null +++ b/src/Helper/SortingLinkGenerator.php @@ -0,0 +1,260 @@ + + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +namespace MetaModels\Helper; + +use Contao\Input; +use MetaModels\Attribute\IAttribute; +use MetaModels\Filter\FilterUrl; +use MetaModels\Filter\FilterUrlBuilder; +use RuntimeException; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function strtolower; + +/** + * Provide methods to generate sorting links. + */ +class SortingLinkGenerator +{ + /** + * The filter url builder. + * + * @var FilterUrlBuilder + */ + private FilterUrlBuilder $urlBuilder; + + /** + * The translator interface. + * + * @var TranslatorInterface + */ + private TranslatorInterface $translator; + + /** + * The sorting type parameter. + * + * @var string + */ + private string $sortParamType; + + /** + * The sorting by parameter. + * + * @var string + */ + private string $sortOrderByParam; + + /** + * The sorting direction type. + * + * @var string + */ + private string $sortOrderDirParam; + + /** + * The URL fragment. + * + * @var string + */ + private string $sortFragment; + + /** + * The default sorting. + * + * @var string + */ + private string $defaultSorting; + + /** + * The default direction. + * + * @var string + */ + private string $defaultDirection; + + /** + * Create a new instance. + * + * @param FilterUrlBuilder $urlBuilder The filter url builder. + * @param TranslatorInterface $translator The translator. + * @param string $sortParamType The pagination parameter url type (slug, get or slugNget). + * @param string $sortOrderByParam The sorting by parameter name. + * @param string $sortOrderDirParam The sorting direction parameter name. + * @param string $sortFragment The URL fragment. + * @param string $defaultSorting The default sorting parameter name. + * @param string $defaultDirection The default sorting direction parameter name. + */ + public function __construct( + FilterUrlBuilder $urlBuilder, + TranslatorInterface $translator, + string $sortParamType, + string $sortOrderByParam, + string $sortOrderDirParam, + string $sortFragment, + string $defaultSorting, + string $defaultDirection, + ) { + $this->urlBuilder = $urlBuilder; + $this->translator = $translator; + $this->sortParamType = $sortParamType; + $this->sortOrderByParam = $sortOrderByParam; + $this->sortOrderDirParam = $sortOrderDirParam; + $this->sortFragment = $sortFragment; + $this->defaultSorting = $defaultSorting; + $this->defaultDirection = \strtolower($defaultDirection); + } + + public function generateSortingLink(IAttribute $attribute, string $type): array + { + $pageFilterUrl = $this->urlBuilder->getCurrentFilterUrl(); + $sortBy = $this->tryReadFromSlugOrGet($pageFilterUrl, $this->sortOrderByParam, $this->sortParamType) + ?? $this->defaultSorting; + $sortDirection = $this->tryReadFromSlugOrGet($pageFilterUrl, $this->sortOrderDirParam, $this->sortParamType) + ?? $this->defaultDirection; + $attributeName = $attribute->getColName(); + $active = $sortBy === $attributeName; + + $dir = $this->determineDirection($type, $active, $sortDirection); + + if ($attributeName === $this->defaultSorting && $dir === $this->defaultDirection) { + $attributeName = ''; + } + + $this->updateSortingInFilterUrl($pageFilterUrl, $attributeName, $dir); + + return [ + 'attribute' => $attribute, + 'name' => $attribute->getName(), + 'href' => $this->urlBuilder->generate($pageFilterUrl) . + ($this->sortFragment ? '#' . $this->sortFragment : ''), + 'direction' => $dir, + 'active' => $active, + 'class' => 'sort' . ($active ? ' active' : '') . ' ' . $dir, + 'label' => $this->translator->trans( + 'MSC.orderMetaModelListBy' . ($dir === 'asc' ? 'Ascending' : 'Descending'), + [0 => $attribute->getName()], + 'contao_default' + ), + ]; + } + + /** + * Determine the direction to use. + * + * @param string $type Type of the link to generate a direction for. + * @param bool $active Flag if the sorting is currently active. + * @param string $sortDirection The current direction (only considered when active). + * + * @return string + */ + private function determineDirection(string $type, bool $active, string $sortDirection): string + { + switch ($type) { + case 'toggle': + // In case of toggle, we override the type with the desired direction. + $type = 'asc'; + if ($active) { + $type = \strtolower($sortDirection) === 'desc' ? 'asc' : 'desc'; + } + // NO break here! + case 'asc': + case 'desc': + return $type; + break; + default: + } + throw new RuntimeException('Unknown link type: ' . $type); + } + + /** + * Get parameter from get or slug. + * + * @param FilterUrl $filterUrl The filter URL to obtain parameters from. + * @param string $sortParam The sort parameter name to obtain. + * @param string $sortType The sort URL type. + * + * @return string|null + */ + private function tryReadFromSlugOrGet(FilterUrl $filterUrl, string $sortParam, string $sortType): ?string + { + $result = null; + + switch ($sortType) { + case 'get': + $result = $this->getGetParam($filterUrl, $sortParam); + break; + case 'slug': + $result = $filterUrl->getSlug($sortParam); + break; + case 'slugNget': + $result = ($this->getGetParam($filterUrl, $sortParam) ?? $filterUrl->getSlug($sortParam)); + break; + default: + } + + // Mark the parameter as used (otherwise, a 404 is thrown) + Input::get($sortParam); + + return $result; + } + + /** + * Retrieve GET parameters. + * + * @param FilterUrl $filterUrl The filter URL. + * @param string $sortParam The sort parameter. + * + * @return string|null + */ + private function getGetParam(FilterUrl $filterUrl, string $sortParam): ?string + { + $value = $filterUrl->getGet($sortParam); + if (\is_array($value)) { + return null; + } + + return $value; + } + + /** + * Write parameter to filter url. + * + * @param FilterUrl $pageFilterUrl The filter url to update. + * @param string $attributeName The name of the attribute to update. + * @param string $dir The direction. + * + * @return void + */ + private function updateSortingInFilterUrl(FilterUrl $pageFilterUrl, string $attributeName, mixed $dir): void + { + if ('get' === $this->sortParamType) { + $pageFilterUrl->setGet($this->sortOrderByParam, $attributeName); + $pageFilterUrl->setGet($this->sortOrderDirParam, $attributeName ? $dir : ''); + } else { + // Use slug or slugNget. + $pageFilterUrl + ->setSlug($this->sortOrderByParam, $attributeName) + ->setGet($this->sortOrderByParam, ''); + $pageFilterUrl + ->setSlug($this->sortOrderDirParam, $attributeName ? $dir : '') + ->setGet($this->sortOrderDirParam, ''); + } + } +} diff --git a/src/Helper/TableManipulation.php b/src/Helper/TableManipulation.php index 801b787c2..fb81c8a75 100644 --- a/src/Helper/TableManipulation.php +++ b/src/Helper/TableManipulation.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 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. @@ -17,7 +17,7 @@ * @author Cliff Parnitzky * @author Richard Henkenjohann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,12 +26,14 @@ use Contao\System; use ContaoCommunityAlliance\DcGeneral\Contao\InputProvider; +use MetaModels\IMetaModelsServiceContainer; use MetaModels\MetaModelsServiceContainer; /** * This is the class for table manipulations like creation/renaming/deleting of tables and columns. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.LongVariable) */ class TableManipulation { @@ -39,7 +41,7 @@ class TableManipulation * SQL statement template to create a table. * First parameter is the table name. */ - const STATEMENT_CREATE_TABLE = ' + public const STATEMENT_CREATE_TABLE = ' CREATE TABLE `%s` ( `id` int(10) unsigned NOT NULL auto_increment, `pid` int(10) unsigned NOT NULL, @@ -52,13 +54,13 @@ class TableManipulation * SQL statement template to rename a table. * First parameter is the old name, second parameter is the new name. */ - const STATEMENT_RENAME_TABLE = 'ALTER TABLE `%s` RENAME TO `%s`'; + public const STATEMENT_RENAME_TABLE = 'ALTER TABLE `%s` RENAME TO `%s`'; /** * SQL statement template to drop a table. * First parameter is the table name of the table to drop. */ - const STATEMENT_DROP_TABLE = 'DROP TABLE `%s`'; + public const STATEMENT_DROP_TABLE = 'DROP TABLE `%s`'; /** * SQL statement template to rename a column of a table. @@ -67,7 +69,7 @@ class TableManipulation * Third parameter is the new name of the column. * Fourth parameter is the new type of the column. */ - const STATEMENT_RENAME_COLUMN = 'ALTER TABLE `%s` CHANGE COLUMN %s %s %s'; + public const STATEMENT_RENAME_COLUMN = 'ALTER TABLE `%s` CHANGE COLUMN %s %s %s'; /** * SQL statement template to add a column to a table. @@ -75,13 +77,13 @@ class TableManipulation * Second parameter is the column name. * Third parameter is the type of the new column. */ - const STATEMENT_CREATE_COLUMN = 'ALTER TABLE `%s` ADD %s %s'; + public const STATEMENT_CREATE_COLUMN = 'ALTER TABLE `%s` ADD %s %s'; /** * SQL statement template to delete a column from a table. * First parameter is the name of the column. */ - const STATEMENT_DROP_COLUMN = 'ALTER TABLE `%s` DROP COLUMN %s'; + public const STATEMENT_DROP_COLUMN = 'ALTER TABLE `%s` DROP COLUMN %s'; /** * SQL statement template to add a index to a column of a table. @@ -89,14 +91,14 @@ class TableManipulation * second parameter is indextype * third parameter is name of the column. */ - const STATEMENT_ADD_INDEX_COLUMN = 'ALTER TABLE `%s` ADD %s(%s)'; + public const STATEMENT_ADD_INDEX_COLUMN = 'ALTER TABLE `%s` ADD %s(%s)'; /** * List of reserved column post fix. * * @var string[] */ - protected static $reservedColumnPostFix = array('__sort'); + protected static $reservedColumnPostFix = ['__sort']; /** * All system columns that always are defined in a MetaModel table. @@ -116,7 +118,11 @@ class TableManipulation */ protected static function getDB() { - return System::getContainer()->get(MetaModelsServiceContainer::class)->getDatabase(); + /** @psalm-suppress DeprecatedClass */ + $serviceContainer = System::getContainer()->get(MetaModelsServiceContainer::class); + assert($serviceContainer instanceof IMetaModelsServiceContainer); + /** @psalm-suppress DeprecatedMethod */ + return $serviceContainer->getDatabase(); } /** @@ -130,14 +136,15 @@ public static function isReserveColumnPostFix($strColName) { $inputProvider = new InputProvider(); - if (!$inputProvider->hasValue('colname') - || strtolower($strColName) !== strtolower($inputProvider->getValue('colname')) + if ( + !$inputProvider->hasValue('colname') + || \strtolower($strColName) !== \strtolower($inputProvider->getValue('colname')) ) { return false; } foreach (self::$reservedColumnPostFix as $postFix) { - if ($postFix !== strtolower(substr($strColName, -strlen($postFix)))) { + if ($postFix !== \strtolower(\substr($strColName, -\strlen($postFix)))) { continue; } @@ -158,7 +165,7 @@ public static function isValidMySQLIdentifier($strName) { // Match for valid table/column name, according to MySQL, a table name must start // with a letter and must be combined of letters, decimals and underscore. - return (1 == preg_match('/^[a-z_][a-z\d_]*$/i', $strName)); + return (1 === \preg_match('/^[a-z_][a-z\d_]*$/i', $strName)); } /** @@ -197,7 +204,7 @@ public static function isValidColumnName($strColName) */ public static function isSystemColumn($strColName) { - return in_array($strColName, $GLOBALS['METAMODELS_SYSTEM_COLUMNS']); + return \in_array($strColName, $GLOBALS['METAMODELS_SYSTEM_COLUMNS']); } /** @@ -215,7 +222,7 @@ public static function isSystemColumn($strColName) public static function checkTablename($strTableName) { if (!self::isValidTablename($strTableName)) { - throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['invalidTableName'], $strTableName)); + throw new \Exception(\sprintf($GLOBALS['TL_LANG']['ERR']['invalidTableName'], $strTableName)); } } @@ -225,7 +232,6 @@ public static function checkTablename($strTableName) * If there is any problem, an Exception is raised, stating the nature of the error in the Exception message. * * @param string $strColName The name of the column. - * * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void @@ -240,11 +246,11 @@ public static function checkTablename($strTableName) public static function checkColumnName($strColName, $blnAllowSystemCol = false) { if (!self::isValidColumnName($strColName)) { - throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['invalidColumnName'], $strColName)); + throw new \Exception(\sprintf($GLOBALS['TL_LANG']['ERR']['invalidColumnName'], $strColName)); } if ((!$blnAllowSystemCol) && self::isSystemColumn($strColName)) { - throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['systemColumn'], $strColName)); + throw new \Exception(\sprintf($GLOBALS['TL_LANG']['ERR']['systemColumn'], $strColName)); } } @@ -263,8 +269,9 @@ public static function checkColumnName($strColName, $blnAllowSystemCol = false) public static function checkTableExists($strTableName) { self::checkTablename($strTableName); + /** @psalm-suppress DeprecatedMethod */ if (!self::getDB()->tableExists($strTableName, null, true)) { - throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['tableDoesNotExist'], $strTableName)); + throw new \Exception(\sprintf($GLOBALS['TL_LANG']['ERR']['tableDoesNotExist'], $strTableName)); } } @@ -283,8 +290,9 @@ public static function checkTableExists($strTableName) public static function checkTableDoesNotExist($strTableName) { self::checkTablename($strTableName); + /** @psalm-suppress DeprecatedMethod */ if (self::getDB()->tableExists($strTableName, null, true)) { - throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['tableExists'], $strTableName)); + throw new \Exception(\sprintf($GLOBALS['TL_LANG']['ERR']['tableExists'], $strTableName)); } } @@ -300,14 +308,14 @@ public static function checkTableDoesNotExist($strTableName) public static function createTable($strTableName) { self::checkTableDoesNotExist($strTableName); - self::getDB()->execute(sprintf(self::STATEMENT_CREATE_TABLE, $strTableName)); + /** @psalm-suppress DeprecatedMethod */ + self::getDB()->execute(\sprintf(self::STATEMENT_CREATE_TABLE, $strTableName)); } /** * Renames a table with the given name to the given new name. * * @param string $strTableName The name of the table to rename. - * * @param string $strNewTableName The name to which the table shall be renamed to. * * @return void @@ -318,8 +326,8 @@ public static function renameTable($strTableName, $strNewTableName) { self::checkTableExists($strTableName); self::checkTableDoesNotExist($strNewTableName); - - self::getDB()->execute(sprintf(self::STATEMENT_RENAME_TABLE, $strTableName, $strNewTableName)); + /** @psalm-suppress DeprecatedMethod */ + self::getDB()->execute(\sprintf(self::STATEMENT_RENAME_TABLE, $strTableName, $strNewTableName)); } /** @@ -334,17 +342,15 @@ public static function renameTable($strTableName, $strNewTableName) public static function deleteTable($strTableName) { self::checkTableExists($strTableName); - - self::getDB()->execute(sprintf(self::STATEMENT_DROP_TABLE, $strTableName)); + /** @psalm-suppress DeprecatedMethod */ + self::getDB()->execute(\sprintf(self::STATEMENT_DROP_TABLE, $strTableName)); } /** * Add a index to given tablename for specified columnname * * @param string $strTableName The table name. - * * @param string $strIndexType The index type. - * * @param string $strColName The column name to add a index. * * @return void @@ -355,8 +361,9 @@ public static function deleteTable($strTableName) public static function addIndex($strTableName, $strIndexType, $strColName) { self::checkColumnExists($strTableName, $strColName); + /** @psalm-suppress DeprecatedMethod */ self::getDB()->execute( - sprintf( + \sprintf( self::STATEMENT_ADD_INDEX_COLUMN, $strTableName, $strIndexType, @@ -369,9 +376,7 @@ public static function addIndex($strTableName, $strIndexType, $strColName) * Checks whether the given table exists. * * @param string $strTableName The table name to check. - * * @param string $strColName The column name to check. - * * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void @@ -386,8 +391,11 @@ public static function checkColumnExists($strTableName, $strColName, $blnAllowSy { self::checkTableExists($strTableName); self::checkColumnName($strColName, $blnAllowSystemCol); + /** @psalm-suppress DeprecatedMethod */ if (!self::getDB()->fieldExists($strColName, $strTableName, true)) { - throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['columnDoesNotExist'], $strColName, $strTableName)); + throw new \Exception( + \sprintf($GLOBALS['TL_LANG']['ERR']['columnDoesNotExist'], $strColName, $strTableName) + ); } } @@ -395,9 +403,7 @@ public static function checkColumnExists($strTableName, $strColName, $blnAllowSy * Checks whether the given column does not exist. * * @param string $strTableName The table name to check. - * * @param string $strColName The column name to check. - * * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void @@ -412,8 +418,9 @@ public static function checkColumnDoesNotExist($strTableName, $strColName, $blnA { self::checkTableExists($strTableName); self::checkColumnName($strColName, $blnAllowSystemCol); + /** @psalm-suppress DeprecatedMethod */ if (self::getDB()->fieldExists($strColName, $strTableName, true)) { - throw new \Exception(sprintf($GLOBALS['TL_LANG']['ERR']['columnExists'], $strColName, $strTableName)); + throw new \Exception(\sprintf($GLOBALS['TL_LANG']['ERR']['columnExists'], $strColName, $strTableName)); } } @@ -423,9 +430,7 @@ public static function checkColumnDoesNotExist($strTableName, $strColName, $blnA * Throws Exception if the table does not exist, the column name is invalid or the column already exists. * * @param string $strTableName The name of the table to add the column to. - * * @param string $strColumnName The name of the new column. - * * @param string $strType The SQL type notation of the new column. * * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. @@ -435,8 +440,9 @@ public static function checkColumnDoesNotExist($strTableName, $strColName, $blnA public static function createColumn($strTableName, $strColumnName, $strType, $blnAllowSystemCol = false) { self::checkColumnDoesNotExist($strTableName, $strColumnName, $blnAllowSystemCol); + /** @psalm-suppress DeprecatedMethod */ self::getDB()->execute( - sprintf( + \sprintf( self::STATEMENT_CREATE_COLUMN, $strTableName, $strColumnName, @@ -451,13 +457,9 @@ public static function createColumn($strTableName, $strColumnName, $strType, $bl * Throws Exception if the table does not exist, the column name is invalid or the column already exists. * * @param string $strTableName The name of the table the column is in. - * * @param string $strColumnName The current name of the column to be renamed. - * * @param string $strNewColumnName The new name for the column. - * * @param string $strNewType The new SQL type notation of the column. - * * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void @@ -473,8 +475,9 @@ public static function renameColumn( self::checkColumnExists($strTableName, $strColumnName, $blnAllowSystemCol); self::checkColumnDoesNotExist($strTableName, $strNewColumnName, $blnAllowSystemCol); } + /** @psalm-suppress DeprecatedMethod */ self::getDB()->execute( - sprintf( + \sprintf( self::STATEMENT_RENAME_COLUMN, $strTableName, $strColumnName, @@ -490,9 +493,7 @@ public static function renameColumn( * Throws Exception if the table does not exist, the column name is invalid or the column does not exist. * * @param string $strTableName The name of the table the column is in. - * * @param string $strColumnName The name of the column to drop. - * * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void @@ -500,8 +501,9 @@ public static function renameColumn( public static function dropColumn($strTableName, $strColumnName, $blnAllowSystemCol = false) { self::checkColumnExists($strTableName, $strColumnName, $blnAllowSystemCol); + /** @psalm-suppress DeprecatedMethod */ self::getDB()->execute( - sprintf( + \sprintf( self::STATEMENT_DROP_COLUMN, $strTableName, $strColumnName @@ -520,7 +522,9 @@ public static function dropColumn($strTableName, $strColumnName, $blnAllowSystem public static function setVariantSupport($strTableName, $blnVariantSupport) { if ($blnVariantSupport) { - if (self::getDB()->tableExists($strTableName, null, true) + /** @psalm-suppress DeprecatedMethod */ + if ( + self::getDB()->tableExists($strTableName, null, true) && (!self::getDB()->fieldExists('varbase', $strTableName, true)) ) { self::createColumn($strTableName, 'varbase', 'char(1) NOT NULL default \'\'', true); @@ -528,10 +532,13 @@ public static function setVariantSupport($strTableName, $blnVariantSupport) // If there is pre-existing data in the table, we need to provide a separate 'vargroup' value to all of // them, we can do this safely by setting all vargroups to the id of the base item. - self::getDB()->execute(sprintf('UPDATE `%s` t SET t.vargroup=id, t.varbase=1', $strTableName)); + /** @psalm-suppress DeprecatedMethod */ + self::getDB()->execute(\sprintf('UPDATE `%s` t SET t.vargroup=id, t.varbase=1', $strTableName)); } } else { - if (self::getDB()->tableExists($strTableName, null, true) + /** @psalm-suppress DeprecatedMethod */ + if ( + self::getDB()->tableExists($strTableName, null, true) && self::getDB()->fieldExists('varbase', $strTableName, true) ) { self::dropColumn($strTableName, 'varbase', true); diff --git a/src/Helper/TableManipulator.php b/src/Helper/TableManipulator.php index 2bd12df8d..bcc799588 100644 --- a/src/Helper/TableManipulator.php +++ b/src/Helper/TableManipulator.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 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. @@ -18,7 +18,7 @@ * @author Cliff Parnitzky * @author Ingolf Steinhardt * @author Richard Henkenjohann - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,6 +27,7 @@ use ContaoCommunityAlliance\DcGeneral\Contao\InputProvider; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\Exceptions\Database\ColumnDoesNotExistException; use MetaModels\Exceptions\Database\ColumnExistsException; use MetaModels\Exceptions\Database\InvalidColumnNameException; @@ -34,10 +35,16 @@ use MetaModels\Exceptions\Database\TableDoesNotExistException; use MetaModels\Exceptions\Database\TableExistsException; +use function in_array; +use function preg_match; +use function sprintf; +use function strtolower; + /** * This is the class for table manipulations like creation/renaming/deleting of tables and columns. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.LongVariable) */ class TableManipulator { @@ -45,7 +52,7 @@ class TableManipulator * SQL statement template to create a table. * First parameter is the table name. */ - const STATEMENT_CREATE_TABLE = ' + public const STATEMENT_CREATE_TABLE = ' CREATE TABLE `%s` ( `id` int(10) unsigned NOT NULL auto_increment, `pid` int(10) unsigned NOT NULL default \'0\', @@ -58,13 +65,13 @@ class TableManipulator * SQL statement template to rename a table. * First parameter is the old name, second parameter is the new name. */ - const STATEMENT_RENAME_TABLE = 'ALTER TABLE `%s` RENAME TO `%s`'; + public const STATEMENT_RENAME_TABLE = 'ALTER TABLE `%s` RENAME TO `%s`'; /** * SQL statement template to drop a table. * First parameter is the table name of the table to drop. */ - const STATEMENT_DROP_TABLE = 'DROP TABLE `%s`'; + public const STATEMENT_DROP_TABLE = 'DROP TABLE `%s`'; /** * SQL statement template to rename a column of a table. @@ -73,7 +80,7 @@ class TableManipulator * Third parameter is the new name of the column. * Fourth parameter is the new type of the column. */ - const STATEMENT_RENAME_COLUMN = 'ALTER TABLE `%s` CHANGE COLUMN `%s` `%s` %s'; + public const STATEMENT_RENAME_COLUMN = 'ALTER TABLE `%s` CHANGE COLUMN `%s` `%s` %s'; /** * SQL statement template to add a column to a table. @@ -81,13 +88,13 @@ class TableManipulator * Second parameter is the column name. * Third parameter is the type of the new column. */ - const STATEMENT_CREATE_COLUMN = 'ALTER TABLE `%s` ADD `%s` %s'; + public const STATEMENT_CREATE_COLUMN = 'ALTER TABLE `%s` ADD `%s` %s'; /** * SQL statement template to delete a column from a table. * First parameter is the name of the column. */ - const STATEMENT_DROP_COLUMN = 'ALTER TABLE `%s` DROP COLUMN `%s`'; + public const STATEMENT_DROP_COLUMN = 'ALTER TABLE `%s` DROP COLUMN `%s`'; /** * SQL statement template to add a index to a column of a table. @@ -95,7 +102,7 @@ class TableManipulator * second parameter is indextype * third parameter is name of the column. */ - const STATEMENT_ADD_INDEX_COLUMN = 'ALTER TABLE `%s` ADD %s(`%s`)'; + public const STATEMENT_ADD_INDEX_COLUMN = 'ALTER TABLE `%s` ADD %s(`%s`)'; /** * List of reserved MySQL identifiers. @@ -104,21 +111,21 @@ class TableManipulator * * @deprecated We work with backticks for table names and columns instead reserved words. */ - protected static $reservedWords = []; + protected static array $reservedWords = []; /** * List of reserved column post fix. * * @var string[] */ - protected static $reservedColumnPostFix = array('__sort'); + protected static array $reservedColumnPostFix = ['__sort']; /** * Database connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * All system columns that always are defined in a MetaModel table. @@ -127,7 +134,7 @@ class TableManipulator * * @var string[] */ - private $systemColumns; + private array $systemColumns; /** * TableManipulator constructor. @@ -151,9 +158,9 @@ public function __construct(Connection $connection, array $systemColumns) * * @deprecated We work with backticks for table names and columns instead reserved words. */ - public function isReservedWord($word) + public function isReservedWord($word): bool { - return in_array(strtoupper($word), self::$reservedWords); + return \in_array(\strtoupper($word), self::$reservedWords); } /** @@ -163,18 +170,19 @@ public function isReservedWord($word) * * @return bool */ - public function isReserveColumnPostFix($strColName) + public function isReserveColumnPostFix($strColName): bool { $inputProvider = new InputProvider(); - if (!$inputProvider->hasValue('colname') - || strtolower($strColName) !== strtolower($inputProvider->getValue('colname')) + if ( + !$inputProvider->hasValue('colname') + || \strtolower($strColName) !== \strtolower($inputProvider->getValue('colname')) ) { return false; } foreach (self::$reservedColumnPostFix as $postFix) { - if ($postFix !== strtolower(substr($strColName, -strlen($postFix)))) { + if ($postFix !== \strtolower(\substr($strColName, -\strlen($postFix)))) { continue; } @@ -191,11 +199,11 @@ public function isReserveColumnPostFix($strColName) * * @return bool */ - public function isValidMySQLIdentifier($strName) + public function isValidMySQLIdentifier(string $strName): bool { // Match for valid table/column name, according to MySQL, a table name must start // with a letter and must be combined of letters, decimals and underscore. - return (1 == preg_match('/^[a-z_][a-z\d_]*$/i', $strName)); + return (1 == \preg_match('/^[a-z_][a-z\d_]*$/i', $strName)); } /** @@ -205,8 +213,9 @@ public function isValidMySQLIdentifier($strName) * * @return bool true if the table name is valid, false otherwise. */ - public function isValidTablename($strTableName) + public function isValidTablename(string $strTableName): bool { + /** @psalm-suppress DeprecatedMethod */ return $this->isValidMySQLIdentifier($strTableName) && !$this->isReservedWord($strTableName); } @@ -217,8 +226,9 @@ public function isValidTablename($strTableName) * * @return bool true if the column is a system column, false otherwise. */ - public function isValidColumnName($strColName) + public function isValidColumnName(string $strColName): bool { + /** @psalm-suppress DeprecatedMethod */ return $this->isValidMySQLIdentifier($strColName) && !$this->isReservedWord($strColName) && !$this->isReserveColumnPostFix($strColName); @@ -231,9 +241,9 @@ public function isValidColumnName($strColName) * * @return bool true if the column is a system column, false otherwise. */ - public function isSystemColumn($strColName) + public function isSystemColumn(string $strColName): bool { - return in_array($strColName, $this->systemColumns); + return \in_array($strColName, $this->systemColumns); } /** @@ -245,7 +255,7 @@ public function isSystemColumn($strColName) * * @throws InvalidTableNameException If an invalid table name has been passed. */ - public function checkTablename($strTableName) + public function checkTablename(string $strTableName): void { if (!$this->isValidTablename($strTableName)) { throw InvalidTableNameException::invalidCharacters($strTableName); @@ -257,9 +267,8 @@ public function checkTablename($strTableName) * * If there is any problem, an Exception is raised, stating the nature of the error in the Exception message. * - * @param string $strColName The name of the column. - * - * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. + * @param string $strColName The name of the column. + * @param bool $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void * @@ -267,7 +276,7 @@ public function checkTablename($strTableName) * * @see{MetaModelTableManipulation::isSystemColumn()} and @see{MetaModelTableManipulation::isValidColumnName()}. */ - public function checkColumnName($strColName, $blnAllowSystemCol = false) + public function checkColumnName(string $strColName, bool $blnAllowSystemCol = false): void { if (!$this->isValidColumnName($strColName)) { throw InvalidColumnNameException::invalidCharacters($strColName); @@ -287,13 +296,14 @@ public function checkColumnName($strColName, $blnAllowSystemCol = false) * * @throws InvalidTableNameException If an invalid table name has been passed. * @throws TableDoesNotExistException If the table does not exist. + * @throws Exception * * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag */ - public function checkTableExists($strTableName) + public function checkTableExists(string $strTableName): void { $this->checkTablename($strTableName); - if (!$this->connection->getSchemaManager()->tablesExist([$strTableName])) { + if (!$this->connection->createSchemaManager()->tablesExist([$strTableName])) { throw TableDoesNotExistException::withName($strTableName); } } @@ -307,13 +317,14 @@ public function checkTableExists($strTableName) * * @throws InvalidTableNameException If an invalid table name has been passed. * @throws TableExistsException If a table with the given name exists. + * @throws Exception * * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag */ - public function checkTableDoesNotExist($strTableName) + public function checkTableDoesNotExist(string $strTableName): void { $this->checkTablename($strTableName); - if ($this->connection->getSchemaManager()->tablesExist([$strTableName])) { + if ($this->connection->createSchemaManager()->tablesExist([$strTableName])) { throw TableExistsException::withName($strTableName); } } @@ -327,18 +338,18 @@ public function checkTableDoesNotExist($strTableName) * * @throws InvalidTableNameException If an invalid table name has been passed. * @throws TableExistsException If a table with the given name exists. + * @throws Exception If query error. */ - public function createTable($strTableName) + public function createTable(string $strTableName): void { $this->checkTableDoesNotExist($strTableName); - $this->connection->query(sprintf(self::STATEMENT_CREATE_TABLE, $strTableName)); + $this->connection->executeQuery(\sprintf(self::STATEMENT_CREATE_TABLE, $strTableName)); } /** * Renames a table with the given name to the given new name. * * @param string $strTableName The name of the table to rename. - * * @param string $strNewTableName The name to which the table shall be renamed to. * * @return void @@ -346,13 +357,14 @@ public function createTable($strTableName) * @throws InvalidTableNameException If an invalid table name has been passed. * @throws TableDoesNotExistException If the source table does not exist. * @throws TableExistsException If a table with the given target name exists. + * @throws Exception If query error. */ - public function renameTable($strTableName, $strNewTableName) + public function renameTable(string $strTableName, string $strNewTableName): void { $this->checkTableExists($strTableName); $this->checkTableDoesNotExist($strNewTableName); - $this->connection->query(sprintf(self::STATEMENT_RENAME_TABLE, $strTableName, $strNewTableName)); + $this->connection->executeQuery(\sprintf(self::STATEMENT_RENAME_TABLE, $strTableName, $strNewTableName)); } /** @@ -364,21 +376,20 @@ public function renameTable($strTableName, $strNewTableName) * * @throws InvalidTableNameException If an invalid table name has been passed. * @throws TableDoesNotExistException If the table does not exist. + * @throws Exception If query error. */ - public function deleteTable($strTableName) + public function deleteTable(string $strTableName): void { $this->checkTableExists($strTableName); - $this->connection->query(sprintf(self::STATEMENT_DROP_TABLE, $strTableName)); + $this->connection->executeQuery(sprintf(self::STATEMENT_DROP_TABLE, $strTableName)); } /** - * Add a index to given tablename for specified columnname + * Add an index to given tablename for specified columnname * * @param string $strTableName The table name. - * * @param string $strIndexType The index type. - * * @param string $strColName The column name to add a index. * * @return void @@ -387,12 +398,13 @@ public function deleteTable($strTableName) * @throws TableDoesNotExistException If the table does not exist. * @throws InvalidColumnNameException If an invalid column name has been passed. * @throws ColumnDoesNotExistException If the column does not exist. + * @throws Exception If query error. */ - public function addIndex($strTableName, $strIndexType, $strColName) + public function addIndex(string $strTableName, string $strIndexType, string $strColName): void { $this->checkColumnExists($strTableName, $strColName); - $this->connection->query( - sprintf( + $this->connection->executeQuery( + \sprintf( self::STATEMENT_ADD_INDEX_COLUMN, $strTableName, $strIndexType, @@ -404,11 +416,9 @@ public function addIndex($strTableName, $strIndexType, $strColName) /** * Checks whether the given table exists. * - * @param string $strTableName The table name to check. - * - * @param string $strColName The column name to check. - * - * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. + * @param string $strTableName The table name to check. + * @param string $strColName The column name to check. + * @param bool $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void * @@ -419,7 +429,7 @@ public function addIndex($strTableName, $strIndexType, $strColName) * * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag */ - public function checkColumnExists($strTableName, $strColName, $blnAllowSystemCol = false) + public function checkColumnExists(string $strTableName, string $strColName, bool $blnAllowSystemCol = false): void { $this->checkTableExists($strTableName); $this->checkColumnName($strColName, $blnAllowSystemCol); @@ -432,21 +442,24 @@ public function checkColumnExists($strTableName, $strColName, $blnAllowSystemCol /** * Checks whether the given column does not exist. * - * @param string $strTableName The table name to check. - * @param string $strColName The column name to check. - * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. + * @param string $strTableName The table name to check. + * @param string $strColName The column name to check. + * @param bool $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void * - * @throws InvalidTableNameException If an invalid table name has been passed. - * @throws TableDoesNotExistException If the table does not exist. - * @throws InvalidColumnNameException If an invalid column name has been passed. - * @throws ColumnExistsException If the column does exist. + * @throws InvalidTableNameException If an invalid table name has been passed. + * @throws TableDoesNotExistException If the table does not exist. + * @throws InvalidColumnNameException If an invalid column name has been passed. + * @throws ColumnExistsException If the column does exist. * * @phpcs:ignore Squiz.Commenting.FunctionCommentThrowTag */ - public function checkColumnDoesNotExist($strTableName, $strColName, $blnAllowSystemCol = false) - { + public function checkColumnDoesNotExist( + string $strTableName, + string $strColName, + bool $blnAllowSystemCol = false + ): void { $this->checkTableExists($strTableName); $this->checkColumnName($strColName, $blnAllowSystemCol); @@ -460,21 +473,24 @@ public function checkColumnDoesNotExist($strTableName, $strColName, $blnAllowSys * * Throws Exception if the table does not exist, the column name is invalid or the column already exists. * - * @param string $strTableName The name of the table to add the column to. - * - * @param string $strColumnName The name of the new column. - * - * @param string $strType The SQL type notation of the new column. - * - * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. + * @param string $strTableName The name of the table to add the column to. + * @param string $strColumnName The name of the new column. + * @param string $strType The SQL type notation of the new column. + * @param bool $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void + * + * @throws Exception If query error. */ - public function createColumn($strTableName, $strColumnName, $strType, $blnAllowSystemCol = false) - { + public function createColumn( + string $strTableName, + string $strColumnName, + string $strType, + bool $blnAllowSystemCol = false + ): void { $this->checkColumnDoesNotExist($strTableName, $strColumnName, $blnAllowSystemCol); - $this->connection->query( - sprintf( + $this->connection->executeQuery( + \sprintf( self::STATEMENT_CREATE_COLUMN, $strTableName, $strColumnName, @@ -488,31 +504,29 @@ public function createColumn($strTableName, $strColumnName, $strType, $blnAllowS * * Throws Exception if the table does not exist, the column name is invalid or the column already exists. * - * @param string $strTableName The name of the table the column is in. - * - * @param string $strColumnName The current name of the column to be renamed. - * - * @param string $strNewColumnName The new name for the column. - * - * @param string $strNewType The new SQL type notation of the column. - * - * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. + * @param string $strTableName The name of the table the column is in. + * @param string $strColumnName The current name of the column to be renamed. + * @param string $strNewColumnName The new name for the column. + * @param string $strNewType The new SQL type notation of the column. + * @param bool $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void + * + * @throws Exception */ public function renameColumn( - $strTableName, - $strColumnName, - $strNewColumnName, - $strNewType, - $blnAllowSystemCol = false - ) { + string $strTableName, + string $strColumnName, + string $strNewColumnName, + string $strNewType, + bool $blnAllowSystemCol = false + ): void { if ($strColumnName != $strNewColumnName) { $this->checkColumnExists($strTableName, $strColumnName, $blnAllowSystemCol); $this->checkColumnDoesNotExist($strTableName, $strNewColumnName, $blnAllowSystemCol); } - $this->connection->query( - sprintf( + $this->connection->executeQuery( + \sprintf( self::STATEMENT_RENAME_COLUMN, $strTableName, $strColumnName, @@ -527,19 +541,19 @@ public function renameColumn( * * Throws Exception if the table does not exist, the column name is invalid or the column does not exist. * - * @param string $strTableName The name of the table the column is in. - * - * @param string $strColumnName The name of the column to drop. - * - * @param boolean $blnAllowSystemCol If this is set to true, no system column name checking will be applied. + * @param string $strTableName The name of the table the column is in. + * @param string $strColumnName The name of the column to drop. + * @param bool $blnAllowSystemCol If this is set to true, no system column name checking will be applied. * * @return void + * + * @throws Exception */ - public function dropColumn($strTableName, $strColumnName, $blnAllowSystemCol = false) + public function dropColumn(string $strTableName, string $strColumnName, bool $blnAllowSystemCol = false): void { $this->checkColumnExists($strTableName, $strColumnName, $blnAllowSystemCol); - $this->connection->query( - sprintf( + $this->connection->executeQuery( + \sprintf( self::STATEMENT_DROP_COLUMN, $strTableName, $strColumnName @@ -551,32 +565,34 @@ public function dropColumn($strTableName, $strColumnName, $blnAllowSystemCol = f * Enables or disables Variant support on a certain MetaModel table. * * @param string $strTableName The table name of the MetaModel. - * * @param bool $blnVariantSupport Flag if the support shall be turned on or off. * * @return void + * + * @throws Exception */ - public function setVariantSupport($strTableName, $blnVariantSupport) + public function setVariantSupport(string $strTableName, bool $blnVariantSupport): void { if ($blnVariantSupport) { - if ($this->connection->getSchemaManager()->tablesExist([$strTableName]) - && (!$this->fieldExists($strTableName, 'varbase'))) { + if ( + $this->connection->createSchemaManager()->tablesExist([$strTableName]) + && (!$this->fieldExists($strTableName, 'varbase')) + ) { $this->createColumn($strTableName, 'varbase', 'char(1) NOT NULL default \'\'', true); $this->createColumn($strTableName, 'vargroup', 'int(11) NOT NULL default 0', true); // If there is pre-existing data in the table, we need to provide a separate 'vargroup' value to all of // them, we can do this safely by setting all vargroups to the id of the base item. $this->connection->executeQuery( - sprintf('UPDATE `%1$s` SET %1$s.vargroup=id, %1$s.varbase=1', $strTableName) + \sprintf('UPDATE `%1$s` SET %1$s.vargroup=id, %1$s.varbase=1', $strTableName) ); } - } else { - if ($this->connection->getSchemaManager()->tablesExist([$strTableName]) - && $this->fieldExists($strTableName, 'varbase') - ) { - $this->dropColumn($strTableName, 'varbase', true); - $this->dropColumn($strTableName, 'vargroup', true); - } + } elseif ( + $this->connection->createSchemaManager()->tablesExist([$strTableName]) + && $this->fieldExists($strTableName, 'varbase') + ) { + $this->dropColumn($strTableName, 'varbase', true); + $this->dropColumn($strTableName, 'vargroup', true); } } @@ -587,10 +603,12 @@ public function setVariantSupport($strTableName, $blnVariantSupport) * @param string $strColumnName Column name. * * @return bool + * + * @throws Exception */ - private function fieldExists($strTableName, $strColumnName) + private function fieldExists(string $strTableName, string $strColumnName): bool { - $columns = $this->connection->getSchemaManager()->listTableColumns($strTableName); + $columns = $this->connection->createSchemaManager()->listTableColumns($strTableName); return isset($columns[$strColumnName]); } diff --git a/src/Helper/ToolboxFile.php b/src/Helper/ToolboxFile.php index fa692be87..bb6b2c19b 100644 --- a/src/Helper/ToolboxFile.php +++ b/src/Helper/ToolboxFile.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -20,13 +20,18 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Andreas Fischer - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\Helper; +use Contao\CoreBundle\Asset\ContaoContext; +use Contao\Model\Collection; +use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; +use ContaoCommunityAlliance\Contao\Bindings\Events\Image\ResizeImageEvent; +use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; use Contao\Controller; use Contao\CoreBundle\Image\ImageFactoryInterface; use Contao\CoreBundle\Image\PictureFactoryInterface; @@ -37,22 +42,22 @@ use Contao\Input; use Contao\LayoutModel; use Contao\PageError403; -use Contao\Picture; use Contao\StringUtil; use Contao\System; use Contao\Validator; -use ContaoCommunityAlliance\Contao\Bindings\ContaoEvents; -use ContaoCommunityAlliance\Contao\Bindings\Events\Image\ResizeImageEvent; -use ContaoCommunityAlliance\UrlBuilder\UrlBuilder; use InvalidArgumentException; use Symfony\Component\Asset\Context\ContextInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBagInterface; +use Symfony\Component\HttpFoundation\Session\Session; /** * This class provides various methods for handling file collection within Contao. * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.ExcessiveClassLength) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) */ class ToolboxFile { @@ -63,98 +68,105 @@ class ToolboxFile * * @deprecated The event dispatcher will get removed in 3.0 as we now use the image factory. */ - private $dispatcher; + private EventDispatcherInterface|null $dispatcher = null; /** * The project root dir. * * @var string */ - private $rootDir; + private string $rootDir; /** * The image factory for resizing. * - * @var ImageFactoryInterface + * @var ImageFactoryInterface|null */ - private $imageFactory; + private ImageFactoryInterface|null $imageFactory = null; /** - * The assets files context. + * The assets file context. * - * @var ContextInterface + * @var ContaoContext */ - private $filesContext; + private ContaoContext $filesContext; /** * The picture factory. * * @var PictureFactoryInterface */ - private $pictureFactory; + private PictureFactoryInterface $pictureFactory; + + /** + * Symfony session object + * + * @var Session + */ + private Session $session; /** * Allowed file extensions. * * @var array */ - protected $acceptedExtensions; + protected array $acceptedExtensions = []; /** * Base language, used for retrieving meta.txt information. * * @var string */ - protected $baseLanguage; + protected string $baseLanguage = ''; /** * The fallback language, used for retrieving meta.txt information. * - * @var string + * @var string|null */ - protected $fallbackLanguage; + protected string|null $fallbackLanguage = ''; /** * Determines if we want to generate images or not. * - * @var boolean + * @var bool */ - protected $blnShowImages; + protected bool $blnShowImages = false; /** * Image resize information. * * @var array */ - protected $resizeImages; + protected array $resizeImages = []; /** * The id to use in lightboxes. * * @var string */ - protected $strLightboxId; + protected string $strLightboxId = ''; /** * The files to process in this instance. * * @var array */ - protected $foundFiles = array(); + protected array $foundFiles = []; /** * The pending paths to collect from DB. * * @var string[] */ - protected $pendingPaths = array(); + protected array $pendingPaths = []; /** * The pending uuids to collect from DB. * * @var array */ - protected $pendingIds = array(); + protected array $pendingIds = []; /** * Flag if download keys shall be generated. @@ -168,36 +180,37 @@ class ToolboxFile * * @var array */ - protected $metaInformation; + protected array $metaInformation = []; /** * File id mapping for files. * * @var string[] */ - protected $uuidMap = array(); + protected array $uuidMap = []; /** * Buffered file information. * * @var array */ - protected $outputBuffer; + protected array $outputBuffer = []; /** * Buffered modification timestamps. * * @var array */ - protected $modifiedTime; + protected array $modifiedTime = []; /** * Create a new instance. * * @param ImageFactoryInterface|EventDispatcherInterface|null $imageFactory The image factory to use. * @param string|null $rootDir The root path of the installation. - * @param ContextInterface|null $filesContext The assets files context. + * @param ContextInterface|null $filesContext The assets file context. * @param PictureFactoryInterface|null $pictureFactory The picture factory. + * @param Session|null $session The session. * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) @@ -205,13 +218,14 @@ class ToolboxFile * @SuppressWarnings(PHPMD.NPathComplexity) */ public function __construct( - $imageFactory = null, + ImageFactoryInterface|EventDispatcherInterface $imageFactory = null, string $rootDir = null, ContextInterface $filesContext = null, - PictureFactoryInterface $pictureFactory = null + PictureFactoryInterface $pictureFactory = null, + Session $session = null ) { switch (true) { - case ($imageFactory instanceof ImageFactoryInterface) && !empty($rootDir): + case ($imageFactory instanceof ImageFactoryInterface) && (null !== $rootDir): $this->imageFactory = $imageFactory; $this->rootDir = $rootDir; break; @@ -223,6 +237,7 @@ public function __construct( E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedProperty */ $this->dispatcher = $imageFactory; break; // This is another deprecated fallback (remove in MetaModels 3.0). @@ -233,43 +248,64 @@ public function __construct( E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $this->dispatcher = System::getContainer()->get('event_dispatcher'); + $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + /** @psalm-suppress DeprecatedProperty */ + $this->dispatcher = $dispatcher; } // Initialize some values to sane base. if (isset($GLOBALS['TL_CONFIG']) && isset($GLOBALS['TL_CONFIG']['allowedDownload'])) { $this->setAcceptedExtensions(StringUtil::trimsplit(',', $GLOBALS['TL_CONFIG']['allowedDownload'])); } - if (null === ($this->rootDir)) { + if (null === $rootDir) { // @codingStandardsIgnoreStart @trigger_error( 'Not passing an "%kernel.project_dir%" parameter is deprecated.', E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $this->rootDir = System::getContainer()->getParameter('kernel.project_dir'); + $rootDir = System::getContainer()->getParameter('kernel.project_dir'); + assert(\is_string($rootDir)); } + $this->rootDir = $rootDir; - if (null === ($this->filesContext = $filesContext)) { + if (null === $filesContext) { // @codingStandardsIgnoreStart @trigger_error( 'Not passing an "ContextInterface" is deprecated.', E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $this->filesContext = System::getContainer()->get('contao.assets.files_context'); + $filesContext = System::getContainer()->get('contao.assets.files_context'); + assert($filesContext instanceof ContextInterface); } + assert($filesContext instanceof ContaoContext); + $this->filesContext = $filesContext; - if (null === ($this->pictureFactory = $pictureFactory)) { + if (null === $pictureFactory) { // @codingStandardsIgnoreStart @trigger_error( 'Not passing an "PictureFactoryInterface" is deprecated.', E_USER_DEPRECATED ); + // @codingStandardsIgnoreEnd + $pictureFactory = System::getContainer()->get('contao.image.picture_factory'); + assert($pictureFactory instanceof PictureFactoryInterface); + } + $this->pictureFactory = $pictureFactory; + if (null === $session) { + // @codingStandardsIgnoreStart + @trigger_error( + 'Not passing a "Session" is deprecated.', + E_USER_DEPRECATED + ); // @codingStandardsIgnoreEnd - $this->pictureFactory = System::getContainer()->get('contao.image.picture_factory'); + $session = System::getContainer()->get('session'); + assert($session instanceof Session); } + $this->session = $session; } /** @@ -287,11 +323,11 @@ public function setAcceptedExtensions($acceptedExtensions) // We must not allow file extensions that are globally disabled. $allowedDownload = StringUtil::trimsplit(',', $GLOBALS['TL_CONFIG']['allowedDownload']); - if (!is_array($acceptedExtensions)) { + if (!\is_array($acceptedExtensions)) { $acceptedExtensions = StringUtil::trimsplit(',', $acceptedExtensions); } - $this->acceptedExtensions = array_map('strtolower', array_intersect($allowedDownload, $acceptedExtensions)); + $this->acceptedExtensions = \array_map('strtolower', \array_intersect($allowedDownload, $acceptedExtensions)); } /** @@ -331,11 +367,11 @@ public function getBaseLanguage() /** * Set the fallback language. * - * @param string $fallbackLanguage The fallback language to use. + * @param string|null $fallbackLanguage The fallback language to use. * - * @return ToolboxFile + * @return static */ - public function setFallbackLanguage($fallbackLanguage) + public function setFallbackLanguage(?string $fallbackLanguage): static { $this->fallbackLanguage = $fallbackLanguage; @@ -345,9 +381,9 @@ public function setFallbackLanguage($fallbackLanguage) /** * Retrieve the fallback language. * - * @return string + * @return string|null */ - public function getFallbackLanguage() + public function getFallbackLanguage(): ?string { return $this->fallbackLanguage; } @@ -357,9 +393,9 @@ public function getFallbackLanguage() * * @param boolean $blnShowImages True to show images, false otherwise. * - * @return ToolboxFile + * @return static */ - public function setShowImages($blnShowImages) + public function setShowImages(bool $blnShowImages): static { $this->blnShowImages = $blnShowImages; @@ -484,40 +520,42 @@ protected function collectFiles() { $table = FilesModel::getTable(); - $conditions = array(); - $parameters = array(); - if (count($this->pendingIds)) { + $conditions = []; + $parameters = []; + if (\count($this->pendingIds)) { $conditions[] = $table . '.uuid IN(' . - implode(',', array_fill(0, count($this->pendingIds), 'UNHEX(?)')) . ')'; - $parameters = array_map('bin2hex', $this->pendingIds); + \implode(',', \array_fill(0, \count($this->pendingIds), 'UNHEX(?)')) . ')'; + $parameters = \array_map('bin2hex', $this->pendingIds); - $this->pendingIds = array(); + $this->pendingIds = []; } - if (count($this->pendingPaths)) { + if ([] !== $this->pendingPaths) { $slug = $table . '.path LIKE ?'; foreach ($this->pendingPaths as $pendingPath) { $conditions[] = $slug; $parameters[] = $pendingPath . '%'; } - $this->pendingPaths = array(); + $this->pendingPaths = []; } - if (!count($conditions)) { + if (!\count($conditions)) { return; } - if ($files = FilesModel::findBy(array(implode(' OR ', $conditions)), $parameters)) { + $files = FilesModel::findBy([\implode(' OR ', $conditions)], $parameters); + if ($files instanceof Collection) { $this->addFileModels($files); } - if (count($this->pendingPaths)) { + /** @psalm-suppress DocblockTypeContradiction - filled addFileModel */ + if ([] !== $this->pendingPaths) { // Run again. $this->collectFiles(); } } /** - * Generate an URL for downloading the given file. + * Generate a URL for downloading the given file. * * @param string $strFile The file that shall be downloaded. * @@ -530,20 +568,25 @@ protected function getDownloadLink($strFile) { if (!$this->withDownloadKeys) { return UrlBuilder::fromUrl(Environment::get('request')) - ->setQueryParameter('file', urlencode($strFile)) + ->setQueryParameter('file', \urlencode($strFile)) ->getUrl(); } - if (isset($_SESSION) && !is_array($_SESSION['metaModels_downloads'])) { - $_SESSION['metaModels_downloads'] = []; - } - if (!isset($_SESSION['metaModels_downloads'][$strFile])) { - $_SESSION['metaModels_downloads'][$strFile] = md5(uniqid()); + $bag = $this->session->getBag('attributes'); + assert($bag instanceof AttributeBagInterface); + + $links = $bag->has('metaModels_downloads') ? $bag->get('metaModels_downloads') : []; + if (!\is_array($links)) { + $links = []; + } + if (!isset($links[$strFile])) { + $links[$strFile] = \md5(\uniqid('', true)); + $bag->set('metaModels_downloads', $links); } return UrlBuilder::fromUrl(Environment::get('request')) ->setQueryParameter('file', urlencode($strFile)) - ->setQueryParameter('fileKey', $_SESSION['metaModels_downloads'][$strFile]) + ->setQueryParameter('fileKey', $links[$strFile]) ->getUrl(); } @@ -557,8 +600,8 @@ protected function getDownloadLink($strFile) */ protected function fetchAdditionalData() { - $this->modifiedTime = array(); - $this->outputBuffer = array(); + $this->modifiedTime = []; + $this->outputBuffer = []; if (!$this->foundFiles) { return; @@ -579,28 +622,26 @@ protected function fetchAdditionalData() * This returns an array like: array('files' => array(), 'source' => array()) * * @param array $arrFiles The files to sort. - * * @param array $arrSource The source list. * * @return array The mapped result. */ protected function remapSorting($arrFiles, $arrSource) { - $files = array(); - $source = array(); + $files = []; + $source = []; - foreach (array_keys($arrFiles) as $k) { + foreach (\array_keys($arrFiles) as $k) { $files[] = $arrFiles[$k]; $source[] = $arrSource[$k]; } $this->addClasses($source); - return array - ( - 'files' => $files, + return [ + 'files' => $files, 'source' => $source - ); + ]; } /** @@ -615,12 +656,11 @@ protected function remapSorting($arrFiles, $arrSource) * random - Shuffle all the files around. * * @param string $sortType The sort condition to be applied. - * * @param array $sortIds The list of binary ids to sort by (sort type "manual" only). * * @return array The sorted file list. */ - public function sortFiles($sortType, $sortIds = array()) + public function sortFiles($sortType, $sortIds = []) { switch ($sortType) { case 'name_desc': @@ -647,17 +687,18 @@ public function sortFiles($sortType, $sortIds = array()) /** * Attach first, last and even/odd classes to the given array. * - * @param array $arrSource The array reference of the array to which the classes shall be added to. + * @param array> $arrSource The array reference of the array to which the classes + * shall be added to. * * @return void */ protected function addClasses(&$arrSource) { - $countFiles = count($arrSource); - foreach (array_keys($arrSource) as $k) { - $arrSource[$k]['class'] = (($k == 0) ? ' first' : '') . - (($k == ($countFiles - 1)) ? ' last' : '') . - ((($k % 2) == 0) ? ' even' : ' odd'); + $countFiles = \count($arrSource); + foreach (\array_keys($arrSource) as $k) { + $arrSource[$k]['class'] = (($k === 0) ? ' first' : '') . + (($k === ($countFiles - 1)) ? ' last' : '') . + ((($k % 2) === 0) ? ' even' : ' odd'); } } @@ -673,7 +714,7 @@ protected function sortByName($blnAscending = true) $arrFiles = $this->foundFiles; if (!$arrFiles) { - return array('files' => array(), 'source' => array()); + return ['files' => [], 'source' => []]; } \uasort($arrFiles, ($blnAscending) ? '\basename_natcasecmp' : '\basename_natcasercmp'); @@ -694,13 +735,13 @@ protected function sortByDate($blnAscending = true) $arrDates = $this->modifiedTime; if (!$arrFiles) { - return array('files' => array(), 'source' => array()); + return ['files' => [], 'source' => []]; } if ($blnAscending) { - array_multisort($arrFiles, SORT_NUMERIC, $arrDates, SORT_ASC); + \array_multisort($arrFiles, SORT_NUMERIC, $arrDates, SORT_ASC); } else { - array_multisort($arrFiles, SORT_NUMERIC, $arrDates, SORT_DESC); + \array_multisort($arrFiles, SORT_NUMERIC, $arrDates, SORT_DESC); } return $this->remapSorting($arrFiles, $this->outputBuffer); @@ -717,10 +758,10 @@ protected function sortByIdList($sortIds) { $fileMap = $this->foundFiles; if (!$fileMap) { - return array('files' => array(), 'source' => array()); + return ['files' => [], 'source' => []]; } - $fileKeys = array_flip(array_keys($this->uuidMap)); - $sorted = array(); + $fileKeys = \array_flip(\array_keys($this->uuidMap)); + $sorted = []; foreach ($sortIds as $sortStringId) { $key = $fileKeys[$sortStringId]; $sorted[$key] = $fileMap[$key]; @@ -743,12 +784,12 @@ protected function sortByRandom() $arrSource = $this->outputBuffer; if (!$arrFiles) { - return array('files' => array(), 'source' => array()); + return ['files' => [], 'source' => []]; } - $keys = array_keys($arrFiles); - $files = array(); - shuffle($keys); + $keys = \array_keys($arrFiles); + $files = []; + \shuffle($keys); foreach ($keys as $key) { $files[$key] = $arrFiles[$key]; } @@ -804,13 +845,24 @@ private function checkDownloads() if ($this->getShowImages()) { return; } + if (($file = Input::get('file'))) { if ($this->withDownloadKeys) { + $bag = $this->session->getBag('attributes'); + assert($bag instanceof AttributeBagInterface); + $links = $bag->has('metaModels_downloads') ? $bag->get('metaModels_downloads') : []; + + if (!\is_array($links)) { + $links = []; + } // Check key and return 403 if mismatch // keep both null-coalescing values different to account for missing values. - if (($_SESSION['metaModels_downloads'][$file] ?? null) !== (Input::get('fileKey') ?? false)) { + if (($links[$file] ?? null) !== (Input::get('fileKey') ?? false)) { $objHandler = new $GLOBALS['TL_PTY']['error_403'](); - /** @var PageError403 $objHandler */ + /** + * @var PageError403 $objHandler + * @psalm-suppress DeprecatedMethod + */ $objHandler->generate($file); } } @@ -834,7 +886,7 @@ public static function convertValueToPath($varValue) $objFiles = FilesModel::findByPk($varValue); - if ($objFiles !== null) { + if ($objFiles instanceof FilesModel) { return $objFiles->path; } return ''; @@ -890,28 +942,31 @@ public static function convertValuesToDatabase($values) */ public static function convertValuesToMetaModels($values) { - if (!is_array($values)) { + /** @psalm-suppress DocblockTypeContradiction */ + if (!\is_array($values)) { throw new InvalidArgumentException('Invalid uuid list.'); } // Convert UUIDs to binary and clean empty values out. - $values = array_filter(array_map(function ($fileId) { - return Validator::isStringUuid($fileId) ? StringUtil::uuidToBin($fileId) : $fileId; - }, $values)); - - $result = array( - 'bin' => array(), - 'value' => array(), - 'path' => array(), - 'meta' => array() + $values = \array_filter( + \array_map(function ($fileId) { + return Validator::isStringUuid($fileId) ? StringUtil::uuidToBin($fileId) : $fileId; + }, $values) ); + + $result = [ + 'bin' => [], + 'value' => [], + 'path' => [], + 'meta' => [] + ]; if (empty($values)) { return $result; } $models = FilesModel::findMultipleByUuids($values); - if ($models === null) { + if (!$models instanceof Collection) { return $result; } @@ -944,14 +999,14 @@ public static function convertValuesToMetaModels($values) */ public static function convertUuidsOrPathsToMetaModels($values) { - $values = array_filter((array) $values); + $values = \array_filter($values); if (empty($values)) { - return array( - 'bin' => array(), - 'value' => array(), - 'path' => array(), - 'meta' => array() - ); + return [ + 'bin' => [], + 'value' => [], + 'path' => [], + 'meta' => [] + ]; } foreach ($values as $key => $value) { @@ -961,7 +1016,7 @@ public static function convertUuidsOrPathsToMetaModels($values) } $file = FilesModel::findByPath($value) ?: Dbafs::addResource($value); - if (!$file) { + if (!($file instanceof FilesModel)) { throw new InvalidArgumentException('Invalid path.'); } @@ -978,22 +1033,28 @@ public static function convertUuidsOrPathsToMetaModels($values) * Must either be called from within collectFiles or collectFiles must be called later on as this method * will add models of type folder to the list of pending paths to allow for recursive inclusion. * - * @param FilesModel[] $files The files to add. - * @param array $skipPaths List of directories not to be added to the list of pending directories. + * @param Collection $files The files to add. + * @param list $skipPaths List of directories not to be added to the list of pending directories. * * @return void + * + * @psalm-assert list $this->pendingPaths */ - private function addFileModels($files, $skipPaths = array()) + private function addFileModels(Collection $files, array $skipPaths = []): void { $baseLanguage = $this->getBaseLanguage(); $fallbackLanguage = $this->getFallbackLanguage(); foreach ($files as $file) { - if ('folder' === $file->type && !in_array($file->path, $skipPaths)) { + if ('folder' === $file->type && !\in_array($file->path, $skipPaths)) { $this->pendingPaths[] = $file->path . '/'; continue; } - if (is_file(TL_ROOT . DIRECTORY_SEPARATOR . $file->path) && - in_array(strtolower(pathinfo($file->path, PATHINFO_EXTENSION)), $this->acceptedExtensions) + if ( + \is_file($this->rootDir . DIRECTORY_SEPARATOR . $file->path) + && \in_array( + \strtolower(pathinfo($file->path, PATHINFO_EXTENSION)), + $this->acceptedExtensions + ) ) { $path = $file->path; $this->foundFiles[] = $path; @@ -1001,9 +1062,9 @@ private function addFileModels($files, $skipPaths = array()) $meta = StringUtil::deserialize($file->meta, true); if (isset($meta[$baseLanguage])) { - $this->metaInformation[dirname($path)][basename($path)] = $meta[$baseLanguage]; + $this->metaInformation[\dirname($path)][\basename($path)] = $meta[$baseLanguage]; } elseif (isset($meta[$fallbackLanguage])) { - $this->metaInformation[dirname($path)][basename($path)] = $meta[$fallbackLanguage]; + $this->metaInformation[\dirname($path)][\basename($path)] = $meta[$fallbackLanguage]; } } } @@ -1017,42 +1078,43 @@ private function addFileModels($files, $skipPaths = array()) * @return void * * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) * @SuppressWarnings(PHPMD.Superglobals) */ private function processFile($fileName) { $file = new File($fileName); - $meta = $this->metaInformation[dirname($fileName)][$file->basename]; - $title = strlen($meta['title']) ? $meta['title'] : StringUtil::specialchars($file->basename); - if (strlen($meta['caption'])) { + $meta = $this->metaInformation[dirname($fileName)][$file->basename] ?? []; + $title = isset($meta['title']) && \strlen($meta['title']) + ? $meta['title'] + : StringUtil::specialchars($file->basename); + if (isset($meta['caption']) && \strlen($meta['caption'])) { $altText = $meta['caption']; } else { - $altText = ucfirst(str_replace('_', ' ', preg_replace('/^[0-9]+_/', '', $file->filename))); + $altText = \ucfirst(\str_replace('_', ' ', \preg_replace('/^[0-9]+_/', '', $file->filename))); } $information = [ 'file' => $fileName, 'mtime' => $file->mtime, 'alt' => $altText, - 'caption' => (!empty($meta['caption']) ? $meta['caption'] : ''), + 'caption' => $meta['caption'] ?? '', 'title' => $title, 'metafile' => $meta, 'icon' => 'assets/contao/images/' . $file->icon, 'extension' => $file->extension, 'size' => $file->filesize, - 'sizetext' => sprintf('(%s)', Controller::getReadableSize($file->filesize, 2)), + 'sizetext' => \sprintf('(%s)', Controller::getReadableSize($file->filesize, 2)), 'url' => StringUtil::specialchars($this->getDownloadLink($fileName)), - 'isGdImage' => false, - 'isSvgImage' => false, 'isPicture' => false, ]; // Prepare GD images. if ($information['isGdImage'] = $file->isGdImage) { - $information['src'] = urldecode($this->resizeImage($fileName)); + $information['src'] = \urldecode($this->resizeImage($fileName)); $information['lb'] = 'lb_' . $this->getLightboxId(); - if (file_exists(TL_ROOT . '/' . $information['src'])) { - $size = getimagesize(TL_ROOT . '/' . $information['src']); + if (\file_exists($this->rootDir . '/' . $information['src'])) { + $size = \getimagesize($this->rootDir . '/' . $information['src']); $information['w'] = $size[0]; $information['h'] = $size[1]; $information['wh'] = $size[3]; @@ -1067,8 +1129,9 @@ private function processFile($fileName) } // Prepare the picture for provide the image size. - if ($file->isImage && ($information['isPicture'] = (int) $this->resizeImages[2])) { + if ($file->isImage && ($information['isPicture'] = (int) ($this->resizeImages[2] ?? 0))) { $projectDir = $this->rootDir; + /** @psalm-suppress InternalMethod */ $staticUrl = $this->filesContext->getStaticUrl(); $picture = $this->pictureFactory->create($projectDir . '/' . $file->path, $this->getResizeImages()); @@ -1102,11 +1165,11 @@ private function processFile($fileName) * * @param string $fileName The file to resize. * - * @return null|string + * @return string */ - private function resizeImage($fileName) + private function resizeImage(string $fileName): string { - [$width, $height, $mode] = $this->getResizeImages(); + [$width, $height, $mode] = $this->getResizeImages() + [null, null, null]; if ($this->getShowImages() && ($width || $height || $mode)) { if ($this->imageFactory) { $image = $this->imageFactory->create( @@ -1118,8 +1181,13 @@ private function resizeImage($fileName) } $event = new ResizeImageEvent($fileName, $width, $height, $mode); - $this->dispatcher->dispatch($event, ContaoEvents::IMAGE_RESIZE); - return $event->getResultImage(); + + /** @psalm-suppress DeprecatedProperty */ + $dispatcher = $this->dispatcher; + assert($dispatcher instanceof EventDispatcherInterface); + $dispatcher->dispatch($event, ContaoEvents::IMAGE_RESIZE); + + return (string) $event->getResultImage(); } return $fileName; diff --git a/src/Helper/UpgradeHandler.php b/src/Helper/UpgradeHandler.php index f5db4ae52..b2a12790c 100644 --- a/src/Helper/UpgradeHandler.php +++ b/src/Helper/UpgradeHandler.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. @@ -13,36 +13,39 @@ * @package MetaModels/core * @author Christian Schiffler * @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\Helper; +use Contao\Database; + /** * Upgrade handler class that changes structural changes in the database. - * This should rarely be necessary but sometimes we need it. + * This should rarely be necessary, but sometimes we need it. */ class UpgradeHandler { /** * Retrieve the database instance from Contao. * - * @return \Database + * @return Database * * @SuppressWarnings(PHPMD.ShortMethodName) * @SuppressWarnings(PHPMD.CamelCaseMethodName) */ protected static function DB() { - return \Database::getInstance(); + return Database::getInstance(); } /** * Handle database upgrade for the jumpTo field. * - * Introduced: pre release 1.0. + * Introduced: pre-release 1.0. * * If the field 'metamodel_jumpTo' does exist in tl_module or tl_content, * it will get created and the content from jumpTo will get copied over. @@ -52,7 +55,8 @@ protected static function DB() protected static function upgradeJumpTo() { $objDB = self::DB(); - if ($objDB->tableExists('tl_content', null, true) + if ( + $objDB->tableExists('tl_content', null, true) && !$objDB->fieldExists('metamodel_jumpTo', 'tl_content', true) ) { // Create the column in the database and copy the data over. @@ -66,7 +70,8 @@ protected static function upgradeJumpTo() } } - if ($objDB->tableExists('tl_module', null, true) + if ( + $objDB->tableExists('tl_module', null, true) && !$objDB->fieldExists('metamodel_jumpTo', 'tl_module', true) ) { // Create the column in the database and copy the data over. @@ -95,7 +100,8 @@ protected static function upgradeJumpTo() protected static function upgradeDcaSettingsPublished() { $objDB = self::DB(); - if ($objDB->tableExists('tl_metamodel_dcasetting', null, true) + if ( + $objDB->tableExists('tl_metamodel_dcasetting', null, true) && !$objDB->fieldExists('published', 'tl_metamodel_dcasetting', true) ) { // Create the column in the database and copy the data over. @@ -137,7 +143,8 @@ protected static function changeSubPalettesToConditions() ); } - if ($objDB->tableExists('tl_metamodel_dcasetting', null, true) + if ( + $objDB->tableExists('tl_metamodel_dcasetting', null, true) && $objDB->fieldExists('subpalette', 'tl_metamodel_dcasetting', true) ) { $subpalettes = $objDB->execute('SELECT * FROM tl_metamodel_dcasetting WHERE subpalette!=0'); @@ -155,6 +162,7 @@ protected static function changeSubPalettesToConditions() $attr = array(); while ($attributes->next()) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ $attr[$attributes->attr_id] = $attributes->colName; } @@ -168,25 +176,27 @@ protected static function changeSubPalettesToConditions() $check = array(); while ($checkboxes->next()) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ $check[$checkboxes->id] = $checkboxes->attr_id; } while ($subpalettes->next()) { // Add property value condition for parent property dependency. - $data = array( - 'pid' => 0, + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $data = [ + 'pid' => 0, 'settingId' => $subpalettes->id, - 'sorting' => '128', - 'tstamp' => time(), - 'enabled' => '1', - 'type' => 'conditionpropertyvalueis', - 'attr_id' => $check[$subpalettes->subpalette], - 'comment' => sprintf( + 'sorting' => '128', + 'tstamp' => \time(), + 'enabled' => '1', + 'type' => 'conditionpropertyvalueis', + 'attr_id' => $check[$subpalettes->subpalette], + 'comment' => \sprintf( 'Only show when checkbox "%s" is checked', $attr[$check[$subpalettes->subpalette]] ), - 'value' => '1', - ); + 'value' => '1', + ]; $objDB ->prepare('INSERT INTO tl_metamodel_dcasetting_condition %s') @@ -217,8 +227,10 @@ protected static function upgradeClosed() $objDB = self::DB(); // Change isclosed to iseditable, iscreatable and isdeleteable. - if ($objDB->tableExists('tl_metamodel_dca', null, true) - && !$objDB->fieldExists('iseditable', 'tl_metamodel_dca')) { + if ( + $objDB->tableExists('tl_metamodel_dca', null, true) + && !$objDB->fieldExists('iseditable', 'tl_metamodel_dca') + ) { // Create the column in the database and copy the data over. TableManipulation::createColumn( 'tl_metamodel_dca', @@ -321,26 +333,28 @@ protected static function upgradeInputScreenFlag() while ($dca->next()) { $renderGroupLen = 0; - if (in_array($dca->flag, array(1,2,3,4))) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + if (\in_array($dca->flag, [1, 2, 3, 4])) { $renderGroupType = 'char'; - if (in_array($dca->flag, array(1,2))) { + if (\in_array($dca->flag, [1, 2])) { $renderGroupLen = 1; } else { $renderGroupLen = 2; } - } elseif (in_array($dca->flag, array(5,6))) { + } elseif (\in_array($dca->flag, [5, 6])) { $renderGroupType = 'day'; - } elseif (in_array($dca->flag, array(7,8))) { + } elseif (\in_array($dca->flag, [7, 8])) { $renderGroupType = 'month'; - } elseif (in_array($dca->flag, array(9,10))) { + } elseif (\in_array($dca->flag, [9, 10])) { $renderGroupType = 'year'; - } elseif (in_array($dca->flag, array(11,12))) { + } elseif (\in_array($dca->flag, [11, 12])) { $renderGroupType = 'digit'; } else { $renderGroupType = 'none'; } - $data = array( + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $data = [ 'pid' => $dca->id, 'sorting' => 128, 'tstamp' => time(), @@ -350,9 +364,9 @@ protected static function upgradeInputScreenFlag() 'rendergrouptype' => $renderGroupType, 'rendergrouplen' => $renderGroupLen, 'rendergroupattr' => 0, - 'rendersort' => in_array($dca->flag, array(2,4,6,8,10,12)) ? 'desc' : 'asc', + 'rendersort' => \in_array($dca->flag, [2, 4, 6, 8, 10, 12]) ? 'desc' : 'asc', 'rendersortattr' => 0, - ); + ]; $objDB ->prepare('INSERT INTO tl_metamodel_dca_sortgroup %s') diff --git a/src/Helper/ViewCombinations.php b/src/Helper/ViewCombinations.php index 38cfbd33a..d8cb4637d 100644 --- a/src/Helper/ViewCombinations.php +++ b/src/Helper/ViewCombinations.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Sven Baumann * @author Ingolf Steinhardt * @author David Molineus - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,10 +24,13 @@ use Contao\System; use Contao\User; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\BackendIntegration\InputScreen\IInputScreen; use MetaModels\BackendIntegration\InputScreen\InputScreen; use MetaModels\IMetaModelsServiceContainer; +use Symfony\Component\HttpFoundation\Request; /** * Class ViewCombinations. @@ -35,13 +38,15 @@ * Retrieve combinations of view and input screens for the currently logged in user (either frontend or backend). * * @deprecated This will get removed. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ abstract class ViewCombinations { - const COMBINATION = 'combination'; - const INPUTSCREEN = 'inputscreen'; - const RENDERSETTING = 'rendersetting'; - const MODELID = 'metamodel'; + public const COMBINATION = 'combination'; + public const INPUTSCREEN = 'inputscreen'; + public const RENDERSETTING = 'rendersetting'; + public const MODELID = 'metamodel'; /** * All MetaModel combinations for lookup. @@ -85,14 +90,14 @@ abstract class ViewCombinations * * @var User */ - private $user; + private User $user; /** * Database connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * Create a new instance. @@ -119,19 +124,31 @@ public function __construct(IMetaModelsServiceContainer $container, User $user, ); // @codingStandardsIgnoreEnd $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); } - $this->connection = $connection; } /** * Try to load the combinations from cache. * - * @return string|null + * @return string */ protected function calculateCacheKey() { - $key = 'view_combination_' . strtolower(TL_MODE); + $tlMode = 'be'; + + if ( + (bool) System::getContainer() + ->get('contao.routing.scope_matcher') + ?->isFrontendRequest( + System::getContainer()->get('request_stack')?->getCurrentRequest() ?? Request::create('') + ) + ) { + $tlMode = 'fe'; + } + + $key = 'view_combination_' . $tlMode; // Authenticate the user - if this fails, we use an anonymous cache file. if ($this->authenticateUser()) { @@ -173,7 +190,7 @@ protected function loadFromCache() } // Perform loading now. - $data = json_decode($this->container->getCache()->fetch($key), true); + $data = \json_decode($this->container->getCache()->fetch($key), true); if (empty($data)) { return false; @@ -196,13 +213,13 @@ protected function saveToCache() { // Pretty print only came available with php 5.4. $flags = 0; - if (defined('JSON_PRETTY_PRINT')) { + if (\defined('JSON_PRETTY_PRINT')) { $flags = JSON_PRETTY_PRINT; } return $this->container->getCache()->save( $this->calculateCacheKey(), - json_encode( + \json_encode( array( 'information' => $this->information, 'tableMap' => $this->tableMap, @@ -217,7 +234,7 @@ protected function saveToCache() /** * Translate the MetaModel id to a valid table name. * - * @param int $metaModelId The id of the MetaModel to translate. + * @param string $metaModelId The id of the MetaModel to translate. * * @return string */ @@ -247,15 +264,12 @@ protected function resolve() ); } - $found = $this->getPaletteCombinationRows(); - - if (!$found) { - $found = array(); - } + $this->getPaletteCombinationRows(); // Clean any undefined. - foreach (array_keys($this->information) as $tableName) { - if (empty($this->information[$tableName][self::COMBINATION]) + foreach (\array_keys($this->information) as $tableName) { + if ( + empty($this->information[$tableName][self::COMBINATION]) || empty($this->information[$tableName][self::COMBINATION]['dca_id']) || empty($this->information[$tableName][self::COMBINATION]['view_id']) ) { @@ -296,7 +310,7 @@ protected function getMetaModelName($nameOrId) /** * Retrieve the Contao database singleton instance. * - * @return \Database + * @return \Contao\Database */ protected function getDatabase() { @@ -320,7 +334,7 @@ abstract protected function authenticateUser(); protected function getUserGroups() { /** @noinspection PhpUndefinedFieldInspection */ - return $this->user->groups ? array_filter($this->user->groups) : array(); + return $this->user->groups ? \array_filter($this->user->groups) : []; } @@ -336,16 +350,28 @@ protected function getCombinationsFromDatabase() return null; } + $tlMode = 'be'; + + if ( + System::getContainer() + ->get('contao.routing.scope_matcher') + ?->isFrontendRequest( + System::getContainer()->get('request_stack')?->getCurrentRequest() ?? Request::create('') + ) + ) { + $tlMode = 'fe'; + } + $statement = $this->connection->createQueryBuilder() ->select('t.*') ->from('tl_metamodel_dca_combine', 't') - ->where('t.' . strtolower(TL_MODE) . '_group IN (:groups)') + ->where('t.' . $tlMode . '_group IN (:groups)') ->orderBy('t.pid') ->addOrderBy('t.sorting', 'ASC') ->setParameter('groups', $groups) - ->execute(); + ->executeQuery(); - return $statement->fetchAll(\PDO::FETCH_OBJ); + return $statement->fetchAllAssociative(); } /** @@ -353,21 +379,21 @@ protected function getCombinationsFromDatabase() * * This returns the ids that have been resolved. * - * @return int[] + * @return list */ protected function getPaletteCombinationRows() { $combinations = $this->getCombinationsFromDatabase(); - $success = array(); + $success = []; // No combinations present, nothing to resolve. if (!$combinations) { - return array_keys($this->information); + return \array_keys($this->information); } foreach ($combinations as $combination) { /** @noinspection PhpUndefinedFieldInspection */ - $modelId = $combination->pid; + $modelId = (string) $combination['pid']; $modelName = $this->tableNameFromId($modelId); // Already a combination present, continue with next one. @@ -379,10 +405,10 @@ protected function getPaletteCombinationRows() $this->information[$modelName][self::MODELID] = $modelId; /** @noinspection PhpUndefinedFieldInspection */ - $this->information[$modelName][self::COMBINATION] = array( - 'dca_id' => $combination->dca_id, - 'view_id' => $combination->view_id - ); + $this->information[$modelName][self::COMBINATION] = [ + 'dca_id' => $combination['dca_id'], + 'view_id' => $combination['view_id'] + ]; $this->setTableMapping($modelId, $modelName); @@ -398,7 +424,7 @@ protected function getPaletteCombinationRows() * * @return void * - * @throws \Doctrine\DBAL\DBALException When a database error occurs. + * @throws Exception */ protected function fetchInputScreenDetails() { @@ -416,14 +442,14 @@ protected function fetchInputScreenDetails() ->select('t.*') ->from('tl_metamodel_dca', 't') ->where('t.id IN (:ids)') - ->setParameter('id', $inputScreenIds, Connection::PARAM_STR_ARRAY) - ->execute(); + ->setParameter('id', $inputScreenIds, ArrayParameterType::STRING) + ->executeQuery(); - while ($inputScreens = $statement->fetch(\PDO::FETCH_OBJ)) { + while (false !== ($inputScreens = $statement->fetchAssociative())) { /** @noinspection PhpUndefinedFieldInspection */ - $screenId = $inputScreens->id; + $screenId = $inputScreens['id']; /** @noinspection PhpUndefinedFieldInspection */ - $metaModelId = $inputScreens->pid; + $metaModelId = (string) $inputScreens['pid']; $metaModelName = $this->tableNameFromId($metaModelId); $propertyRows = $this->connection @@ -434,11 +460,11 @@ protected function fetchInputScreenDetails() ->andWhere('t.published=1') ->setParameter('id', $screenId) ->orderBy('t.sorting', 'ASC') - ->execute(); + ->executeQuery(); $conditions = $this->connection ->createQueryBuilder() - ->select('cond.*, setting.attr_id AS setting_attr_id') + ->select('cond.*', 'setting.attr_id AS setting_attr_id') ->from('tl_metamodel_dcasetting_condition', 'cond') ->leftJoin('cond', 'tl_metamodel_dcasetti', 'setting', 'cond.settingId=setting.id') ->leftJoin('setting', 'tl_metamodel_dca', 'dca', 'setting.pid=dca.id') @@ -447,7 +473,7 @@ protected function fetchInputScreenDetails() ->andWhere('cond.enabled=1') ->setParameter('id', $screenId) ->orderBy('cond.sorting', 'ASC') - ->execute(); + ->executeQuery(); $groupSort = $this->connection ->createQueryBuilder() @@ -456,14 +482,14 @@ protected function fetchInputScreenDetails() ->where('t.pid=:pid') ->setParameter('pid', $screenId) ->orderBy('t.sorting', 'ASC') - ->execute(); + ->executeQuery(); - $inputScreen = array( + $inputScreen = [ 'row' => $inputScreens->row(), - 'properties' => $propertyRows->fetchAll(\PDO::FETCH_ASSOC), - 'conditions' => $conditions->fetchAll(\PDO::FETCH_ASSOC), - 'groupSort' => $groupSort->fetchAll(\PDO::FETCH_ASSOC) - ); + 'properties' => $propertyRows->fetchAllAssociative(), + 'conditions' => $conditions->fetchAllAssociative(), + 'groupSort' => $groupSort->fetchAllAssociative() + ]; $this->information[$metaModelName][self::INPUTSCREEN] = $inputScreen; $this->information[$metaModelName][self::MODELID] = $metaModelId; @@ -488,7 +514,7 @@ protected function buildInputScreen($metaModel) $metaModelName = $this->getMetaModelName($metaModel); $inputScreen = $this->information[$metaModelName][self::INPUTSCREEN]; - if (!is_object($inputScreen)) { + if (!\is_object($inputScreen)) { $inputScreen = $this->information[$metaModelName][self::INPUTSCREEN] = new InputScreen( $this->container, $inputScreen['row'], @@ -512,8 +538,8 @@ protected function isInputScreenStandalone($metaModel) { $information = $this->information[$metaModel]; $inputScreen = isset($information[self::INPUTSCREEN]) ? $information[self::INPUTSCREEN] : null; - if (!is_object($inputScreen)) { - return ($inputScreen['row']['rendertype'] == 'standalone'); + if (!\is_object($inputScreen)) { + return ($inputScreen['row']['rendertype'] === 'standalone'); } /** @var IInputScreen $inputScreen */ @@ -531,7 +557,7 @@ public function getRenderSetting($metaModel) { $metaModelName = $this->getMetaModelName($metaModel); - return ($this->information[$metaModelName][self::COMBINATION]['view_id'] ?? null); + return ($this->information[$metaModelName][self::COMBINATION]['view_id'] ?? 0); } /** @@ -543,8 +569,7 @@ public function getRenderSetting($metaModel) */ public function getInputScreen($metaModel) { - $inputScreen = $this->getInputScreenDetails($metaModel); - return $inputScreen ? $inputScreen->getId() : null; + return $this->getInputScreenDetails($metaModel)->getId(); } /** @@ -567,7 +592,7 @@ public function getInputScreenDetails($metaModel) public function getStandaloneInputScreens() { $result = []; - foreach (array_keys($this->information) as $modelName) { + foreach (\array_keys($this->information) as $modelName) { if ($this->isInputScreenStandalone($modelName)) { $result[] = $this->getInputScreenDetails($modelName); } @@ -587,7 +612,7 @@ public function getParentOf($metaModel) { $metaModelName = $this->getMetaModelName($metaModel); - return ($this->childMap[$metaModelName] ?? null); + return ($this->childMap[$metaModelName] ?? ''); } /** @@ -599,9 +624,9 @@ public function getParentOf($metaModel) */ public function getParentedInputScreenNames($parent = null) { - $result = array(); + $result = []; - if ($parent) { + if (null !== $parent) { if (!isset($this->parentMap[$parent])) { return []; } @@ -612,7 +637,7 @@ public function getParentedInputScreenNames($parent = null) return $result; } - foreach (array_keys($this->information) as $modelName) { + foreach (\array_keys($this->information) as $modelName) { if (!$this->isInputScreenStandalone($modelName)) { $result[] = (isset($this->tableMap[$modelName])) ? $this->tableMap[$modelName] : $modelName; } @@ -630,7 +655,7 @@ public function getParentedInputScreenNames($parent = null) */ public function getParentedInputScreens($parent = null) { - $result = array(); + $result = []; foreach ($this->getParentedInputScreenNames($parent) as $modelName) { $result[] = $this->getInputScreenDetails($modelName); diff --git a/src/IFactory.php b/src/IFactory.php index 8c8c666df..5c39baf5d 100644 --- a/src/IFactory.php +++ b/src/IFactory.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -37,6 +38,8 @@ interface IFactory extends IServiceContainerAware * @return IMetaModelsServiceContainer * * @deprecated The service container will get removed, use the symfony service container instead. + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer(); diff --git a/src/IItem.php b/src/IItem.php index e19098003..589102a7f 100644 --- a/src/IItem.php +++ b/src/IItem.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author Stefan Heimes * @author Richard Henkenjohann * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -42,7 +43,6 @@ public function get($strAttributeName); * Set the native value of an attribute. * * @param string $strAttributeName The name of the attribute. - * * @param mixed $varValue The value of the attribute. * * @return \MetaModels\IItem @@ -50,18 +50,18 @@ public function get($strAttributeName); public function set($strAttributeName, $varValue); /** - * Fetch the meta model that represents this item. + * Fetch the MetaModel that represents this item. * * @return \MetaModels\IMetaModel The instance. */ public function getMetaModel(); /** - * Fetch the meta model attribute instance with the given name. + * Fetch the MetaModel attribute instance with the given name. * * @param string $strAttributeName The name of the attribute. * - * @return \MetaModels\Attribute\IAttribute The instance. + * @return \MetaModels\Attribute\IAttribute|null the instance or null if not found. */ public function getAttribute($strAttributeName); @@ -88,7 +88,7 @@ public function getSetAttributes(); /** * Determines if this item is a variant of another item. * - * @return bool True if it is an variant, false otherwise + * @return bool True if it is a variant, false otherwise */ public function isVariant(); @@ -99,12 +99,12 @@ public function isVariant(); * this item. It merely simply states, that this item is able * to function as variant base for other items. * - * @return bool true if it is an variant base, false otherwise. + * @return bool true if it is a variant base, false otherwise. */ public function isVariantBase(); /** - * Fetch the meta model variants for this item. + * Fetch the MetaModel variants for this item. * * @param \MetaModels\Filter\IFilter $objFilter The filter settings to be applied. * @@ -113,7 +113,7 @@ public function isVariantBase(); public function getVariants($objFilter); /** - * Fetch the meta model variant base for this item. + * Fetch the MetaModel variant base for this item. * * Note: For a non-variant item the variant base is the item itself. * @@ -138,7 +138,6 @@ public function save($timestamp = null); * For further information {@see IMetaModelAttribute::parseValue()}. * * @param string $strOutputFormat Optional, the desired output format (default: text). - * * @param ICollection $objSettings The render settings to use optional (default: null). * * @return array attribute name => format => value @@ -151,9 +150,7 @@ public function parseValue($strOutputFormat = 'text', $objSettings = null); * For further information {@see IMetaModelAttribute::parseValue()}. * * @param string $strAttributeName The desired attribute. - * * @param string $strOutputFormat Optional, the desired output format (default: text). - * * @param ICollection $objSettings The render settings to use optional (default: null). * * @return array format=>value @@ -175,7 +172,7 @@ public function copy(); * Additionally, the item will be a variant child of this item. * * NOTE: if this item is not a variant base itself, this item will return a item - * that is a child of this items variant base. i.e. exact clone. + * that is a child of these items variant base. i.e. exact clone. * * @return \MetaModels\IItem the new copy. */ diff --git a/src/IItems.php b/src/IItems.php index 06b050e7f..82198d599 100644 --- a/src/IItems.php +++ b/src/IItems.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author David Maack * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -28,13 +29,15 @@ * * @method IItems|bool next() Advance the internal cursor by one returns the current instance or false when last * item has had been reached. + * @extends \Iterator + * @extends \ArrayAccess */ interface IItems extends \Iterator, \ArrayAccess { /** * Return the current item. * - * @return IItem + * @return IItem|null */ public function getItem(); @@ -90,7 +93,6 @@ public function getClass(); * Parses the current item in the desired output format using the format settings. * * @param string $strOutputFormat Optional, defaults to text. The output format to use. - * * @param ICollection|null $objSettings Optional, defaults to null. The additional settings. * * @return array the parsed information. @@ -101,7 +103,6 @@ public function parseValue($strOutputFormat = 'text', $objSettings = null); * Parses all items in the desired output format using the format settings. * * @param string $strOutputFormat Optional, defaults to text. The output format to use. - * * @param ICollection|null $objSettings Optional, defaults to null. The additional settings. * * @return array the parsed information. diff --git a/src/IMetaModel.php b/src/IMetaModel.php index 65e44ac96..de02bf631 100644 --- a/src/IMetaModel.php +++ b/src/IMetaModel.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author David Maack * @author Ingolf Steinhardt * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,6 +24,7 @@ use MetaModels\Attribute\IAttribute; use MetaModels\Filter\IFilter; +use MetaModels\Render\Setting\ICollection; /** * This is the main MetaModel interface. @@ -31,9 +32,9 @@ * This interface handles all attribute definition instantiation and can be queried for a view instance to certain * entries. * - * @see MetaModelFactory::byId To instantiate a MetaModel by its ID. + * @see MetaModelFactory::byId To instantiate a MetaModel by its ID. * - * @see \MetaModels\IFactory::getMetaModel($metaModelName) To instantiate a MetaModel by its table name. + * @see IFactory::getMetaModel To instantiate a MetaModel by its table name. */ interface IMetaModel { @@ -43,6 +44,8 @@ interface IMetaModel * @return IMetaModelsServiceContainer * * @deprecated Inject services via constructor or setter. + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer(); @@ -82,7 +85,7 @@ public function get($strKey); public function getTableName(); /** - * Retrieve the human readable name for this MetaModel. + * Retrieve the human-readable name for this MetaModel. * * @return string the name for the MetaModel. */ @@ -121,8 +124,8 @@ public function hasVariants(); /** * Fetches all language codes that have been marked as available for translation in this MetaModel. * - * @return string[]|null An array containing all codes if the MetaModel is translated, - * null if translation is not active. + * @return list|null An array containing all codes if the MetaModel is translated, + * null if translation is not active. * * @deprecated Please implement ITranslatedMetaModel instead. */ @@ -152,7 +155,7 @@ public function getActiveLanguage(); * * @param string $strAttributeName The name of the attribute to search. * - * @return IAttribute the instance or null if not found. + * @return null|IAttribute the instance or null if not found. */ public function getAttribute($strAttributeName); @@ -166,34 +169,28 @@ public function getAttribute($strAttributeName); public function getAttributeById($intId); /** - * Search the MetaModel for the item with the given Id. - * - * @param int $intId The Id to be searched. + * Search the MetaModel for the item with the given id. * - * @param string[] $arrAttrOnly Names of the attributes that shall be enclosed in the result, defaults to empty - * which means all attributes. + * @param string $intId The id to be searched. + * @param list $arrAttrOnly Names of the attributes that shall be enclosed in the result, defaults to empty + * which means all attributes. * * @return IItem|null The item if found, NULL otherwise. */ - public function findById($intId, $arrAttrOnly = array()); + public function findById($intId, $arrAttrOnly = []); /** * Filter the MetaModel by the provided filter settings. * * @param IFilter|null $objFilter The filter object to use or null if none. - * * @param string $strSortBy Optional name of the attribute the entries shall be sorted. - * * @param int $intOffset Optional offset for the first item. - * * @param int $intLimit Optional amount of items to retrieve. - * * @param string $strSortOrder Optional sorting direction, either 'ASC'(default) or 'DESC'. - * - * @param string[] $arrAttrOnly Names of the attributes that shall be enclosed in the result, defaults to + * @param list $arrAttrOnly Names of the attributes that shall be enclosed in the result, defaults to * empty which means all attributes. * - * @return IItems|IItem[] The collection of IItem instances that match the given filter. + * @return IItems The collection of IItem instances that match the given filter. */ public function findByFilter( $objFilter, @@ -201,23 +198,19 @@ public function findByFilter( $intOffset = 0, $intLimit = 0, $strSortOrder = 'ASC', - $arrAttrOnly = array() + $arrAttrOnly = [] ); /** * Filter the MetaModel by the provided filter settings and return the ids of all matching items. * * @param IFilter|null $objFilter The filter object to use or null if none. - * * @param string $strSortBy Optional name of the attribute the entries shall be sorted. - * * @param int $intOffset Optional offset for the first item. - * * @param int $intLimit Optional amount of items to retrieve. - * * @param string $strSortOrder Optional sorting direction, either 'ASC'(default) or 'DESC'. * - * @return string[] the ids of items that match the given filter. + * @return list the ids of items that match the given filter. */ public function getIdsFromFilter($objFilter, $strSortBy = '', $intOffset = 0, $intLimit = 0, $strSortOrder = 'ASC'); @@ -242,9 +235,8 @@ public function findVariantBase($objFilter); /** * Get variants for the given ids, filtered by the provided filter settings. * - * @param array $arrIds The Ids of the base elements. - * - * @param IFilter $objFilter The filter to use or null if no filtering. + * @param list $arrIds The Ids of the base elements. + * @param IFilter $objFilter The filter to use or null if no filtering. * * @return IItems The collection of IItem instances that match the given filter. */ @@ -253,10 +245,9 @@ public function findVariants($arrIds, $objFilter); /** * Find all variants of the given item. * - * This methods makes no difference between the variant base item and other variants. - * - * @param array $arrIds The Ids of the base elements. + * These methods make no difference between the variant base item and other variants. * + * @param list $arrIds The Ids of the base elements. * @param IFilter|null $objFilter The filter to use or null if no filtering. * * @return IItems the collection of IItem instances that match the given filter. @@ -267,11 +258,10 @@ public function findVariantsWithBase($arrIds, $objFilter); * Get all options of the given attribute. * * @param string $strAttribute The attribute to fetch options from. - * * @param IFilter $objFilter The filter to use or null if no filtering. * - * @return array all options matching the given filter for the given attribute to be usable in a filter select - * widget. + * @return array all options matching the given filter for the given attribute to be usable in a + * filter select widget. */ public function getAttributeOptions($strAttribute, $objFilter = null); @@ -279,7 +269,6 @@ public function getAttributeOptions($strAttribute, $objFilter = null); * Save an item into the database. * * @param IItem $objItem The item to save to the database. - * * @param int|null $timestamp Optional parameter for use own timestamp. * This is useful if save a collection of models and all shall have * the same timestamp. @@ -298,7 +287,7 @@ public function saveItem($objItem, $timestamp = null); public function delete(IItem $objItem); /** - * Prepare an empty filter object for this meta model. The returned filter contains no rules. + * Prepare an empty filter object for this MetaModel. The returned filter contains no rules. * * @return IFilter the filter object. */ @@ -307,9 +296,8 @@ public function getEmptyFilter(); /** * Generates a filter object that takes the given attributes into account. * - * @param int $intFilterSettings The id of the filter settings to use. - * - * @param array $arrFilterUrl The filter url parameters (usually the contents of $_GET etc.). + * @param string $intFilterSettings The id of the filter settings to use. + * @param array $arrFilterUrl The filter url parameters (usually the contents of $_GET etc.). * * @return IFilter the generated filter object. * @@ -322,7 +310,7 @@ public function prepareFilter($intFilterSettings, $arrFilterUrl); * * @param int $intViewId The id of the render settings to retrieve. * - * @return \MetaModels\Render\Setting\ICollection + * @return ICollection * * @deprecated To be removed in MetaModels 3.0 - use the render setting factory instead. */ diff --git a/src/IMetaModelsServiceContainer.php b/src/IMetaModelsServiceContainer.php index 36f3a2d9b..7b4ac53cc 100644 --- a/src/IMetaModelsServiceContainer.php +++ b/src/IMetaModelsServiceContainer.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -109,6 +110,8 @@ public function getCache(); * @return MetaModelsServiceContainer * * @deprecated The service container will get removed, use the symfony service container instead. + * + * @psalm-suppress DeprecatedClass */ public function setService($service, $serviceName = null); diff --git a/src/IServiceContainerAware.php b/src/IServiceContainerAware.php index eec99ea49..d6c8abd0d 100644 --- a/src/IServiceContainerAware.php +++ b/src/IServiceContainerAware.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -23,7 +24,9 @@ /** * Reference implementation of IMetaModelsServiceContainer. * - * @deprecated The interface has been deprecated as the whole service container is deprecated and will get removed. + * @deprecated The interface has been deprecated as the whole service container is deprecated and will get removed. + * + * @psalm-suppress DeprecatedInterface */ interface IServiceContainerAware { @@ -35,6 +38,8 @@ interface IServiceContainerAware * @return IServiceContainerAware * * @deprecated The service container will get removed, use the symfony service container instead. + * + * @psalm-suppress DeprecatedInterface */ public function setServiceContainer(IMetaModelsServiceContainer $serviceContainer); @@ -44,6 +49,8 @@ public function setServiceContainer(IMetaModelsServiceContainer $serviceContaine * @return IMetaModelsServiceContainer|null * * @deprecated The service container will get removed, use the symfony service container instead. + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer(); } diff --git a/src/ITranslatedMetaModel.php b/src/ITranslatedMetaModel.php index 454d72278..a42ec4c25 100644 --- a/src/ITranslatedMetaModel.php +++ b/src/ITranslatedMetaModel.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2018 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/core * @author Christian Schiffler - * @copyright 2012-2018 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -27,7 +28,7 @@ interface ITranslatedMetaModel extends IMetaModel /** * Fetches all language codes that have been marked as available for translation in this MetaModel. * - * @return string[] An array containing all codes. + * @return list An array containing all codes. */ public function getLanguages(): array; diff --git a/src/Information/AttributeInformation.php b/src/Information/AttributeInformation.php new file mode 100644 index 000000000..cb5e04115 --- /dev/null +++ b/src/Information/AttributeInformation.php @@ -0,0 +1,74 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Information; + +/** + * This is a generic attribute information. + */ +class AttributeInformation implements AttributeInformationInterface +{ + use ConfigurationTrait; + + /** + * The name of the attribute. + * + * @var string + */ + private $name; + + /** + * The type of the attribute. + * + * @var string + */ + private $type; + + /** + * Create a new instance. + * + * @param string $name The name of the attribute. + * @param string $type The type name. + * @param array $configuration The initial configuration. + */ + public function __construct(string $name, string $type, array $configuration = []) + { + $this->name = $name; + $this->type = $type; + $this->configuration = $configuration; + } + + /** + * {@inheritDoc} + */ + public function getName(): string + { + return $this->name; + } + + /** + * {@inheritDoc} + */ + public function getType(): string + { + return $this->type; + } +} diff --git a/src/Information/AttributeInformationInterface.php b/src/Information/AttributeInformationInterface.php new file mode 100644 index 000000000..bd85ed5d0 --- /dev/null +++ b/src/Information/AttributeInformationInterface.php @@ -0,0 +1,69 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Information; + +/** + * This interface describes MetaModel attributes. + */ +interface AttributeInformationInterface +{ + /** + * Obtain the internal name of the attribute. + * + * @return string + */ + public function getName(): string; + + /** + * Get the type name. + * + * @return string + */ + public function getType(): string; + + /** + * Obtain the configuration for the attribute. + * + * @return array + */ + public function getConfiguration(): array; + + /** + * Check if a configuration value exists. + * + * @param string $name The name of the configuration value to test. + * + * @return bool + */ + public function hasConfigurationValue(string $name): bool; + + /** + * Obtain a single configuration value. + * + * @param string $name The name of the configuration value to obtain. + * + * @return mixed + * + * @throws \RuntimeException When the configuration key does not exist. + */ + public function getConfigurationValue(string $name); +} diff --git a/src/Information/ConfigurationTrait.php b/src/Information/ConfigurationTrait.php new file mode 100644 index 000000000..b2171f9c9 --- /dev/null +++ b/src/Information/ConfigurationTrait.php @@ -0,0 +1,89 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Information; + +/** + * This helps writing key/value stores. + */ +trait ConfigurationTrait +{ + /** + * The configuration values. + * + * @var array + */ + private $configuration = []; + + /** + * Retrieve configuration. + * + * @return array + */ + public function getConfiguration(): array + { + return $this->configuration; + } + + /** + * Check if a configuration value exists. + * + * @param string $name The name of the configuration value to test. + * + * @return bool + */ + public function hasConfigurationValue(string $name): bool + { + return array_key_exists($name, $this->configuration); + } + + /** + * Obtain a single configuration value. + * + * @param string $name The name of the configuration value to obtain. + * + * @return mixed + * + * @throws \InvalidArgumentException When the configuration key does not exist. + */ + public function getConfigurationValue(string $name) + { + if (!$this->hasConfigurationValue($name)) { + throw new \InvalidArgumentException('Configuration key "' . $name . '" does not exist'); + } + + return $this->configuration[$name]; + } + + /** + * Add configuration values from the passed array. + * + * @param array $values The new values. + * + * @return void + */ + public function addConfiguration(array $values): void + { + foreach ($values as $key => $value) { + $this->configuration[$key] = $value; + } + } +} diff --git a/src/Information/MetaModelCollection.php b/src/Information/MetaModelCollection.php new file mode 100644 index 000000000..c9578d285 --- /dev/null +++ b/src/Information/MetaModelCollection.php @@ -0,0 +1,103 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Information; + +/** + * This is a collection of MetaModels. + */ +class MetaModelCollection implements MetaModelCollectionInterface +{ + /** + * The list of configured MetaModels. + * + * @var array + */ + private $metaModels = []; + + /** + * {@inheritDoc} + */ + public function getNames(): array + { + return array_keys($this->metaModels); + } + + /** + * {@inheritDoc} + */ + public function all(): array + { + return array_values($this->metaModels); + } + + /** + * Add information for a MetaModel. + * + * @param MetaModelInformationInterface $information The MetaModel information. + * + * @return void + * + * @throws \InvalidArgumentException When the MetaModel is already registered. + */ + public function add(MetaModelInformationInterface $information): void + { + if ($this->has($name = $information->getName())) { + throw new \InvalidArgumentException('MetaModel "' . $name . '" already registered'); + } + + $this->metaModels[$name] = $information; + } + + /** + * {@inheritDoc} + */ + public function has(string $name): bool + { + return array_key_exists($name, $this->metaModels); + } + + /** + * {@inheritDoc} + * + * @throws \InvalidArgumentException When the MetaModel is not registered. + */ + public function get(string $name): MetaModelInformationInterface + { + if (!$this->has($name)) { + throw new \InvalidArgumentException('Unknown MetaModel "' . $name . '"'); + } + + return $this->metaModels[$name]; + } + + /** + * {@inheritDoc} + */ + public function getIterator(): \Traversable + { + // Cannot "yield from" here as we have the names as key. + /** @noinspection YieldFromCanBeUsedInspection */ + foreach ($this->metaModels as $metaModel) { + yield $metaModel; + } + } +} diff --git a/src/Information/MetaModelCollectionInterface.php b/src/Information/MetaModelCollectionInterface.php new file mode 100644 index 000000000..2a319a617 --- /dev/null +++ b/src/Information/MetaModelCollectionInterface.php @@ -0,0 +1,71 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Information; + +/** + * This is a collection of MetaModels. + * + * @extends \IteratorAggregate + */ +interface MetaModelCollectionInterface extends \IteratorAggregate +{ + /** + * Obtain the names of the registered MetaModels. + * + * @return array + */ + public function getNames(): array; + + /** + * Obtain the list of configured MetaModels. + * + * @return array + */ + public function all(): array; + + /** + * Test if a MetaModel is registered. + * + * @param string $name The name of the MetaModel to test. + * + * @return bool + */ + public function has(string $name): bool; + + /** + * Get a MetaModel by name. + * + * @param string $name The name to search. + * + * @return MetaModelInformationInterface + * + * @throws \InvalidArgumentException When the MetaModel is not registered. + */ + public function get(string $name): MetaModelInformationInterface; + + /** + * {@inheritDoc} + * + * @return \Traversable + */ + public function getIterator(): \Traversable; +} diff --git a/src/Information/MetaModelInformation.php b/src/Information/MetaModelInformation.php new file mode 100644 index 000000000..2e0a38cd0 --- /dev/null +++ b/src/Information/MetaModelInformation.php @@ -0,0 +1,135 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Information; + +/** + * This holds the schema information for a MetaModel. + */ +class MetaModelInformation implements MetaModelInformationInterface +{ + use ConfigurationTrait; + + /** + * The name of the attribute. + * + * @var string + */ + private string $name; + + /** + * The array of attribute information. + * + * @var AttributeInformationInterface[] + */ + private array $attributes = []; + + /** + * Create a new instance. + * + * @param string $name The name of the metamodel. + * @param array $configuration The initial configuration. + */ + public function __construct(string $name, array $configuration = []) + { + $this->name = $name; + $this->configuration = $configuration; + } + + /** + * {@inheritDoc} + */ + public function getName(): string + { + return $this->name; + } + + /** + * {@inheritDoc} + */ + public function getAttributeNames(): array + { + return \array_keys($this->attributes); + } + + /** + * Add schema information for an attribute. + * + * @param AttributeInformationInterface $attribute The attribute schema information. + * + * @return void + * + * @throws \InvalidArgumentException When the MetaModel has already been registered. + */ + public function addAttribute(AttributeInformationInterface $attribute): void + { + if ($this->hasAttribute($name = $attribute->getName())) { + throw new \InvalidArgumentException('Attribute "' . $name . '" already registered'); + } + + $this->attributes[$name] = $attribute; + } + + /** + * {@inheritDoc} + * + * @return AttributeInformationInterface + * + * @throws \InvalidArgumentException When the attribute is not registered. + */ + public function getAttribute(string $name): AttributeInformationInterface + { + if (!$this->hasAttribute($name)) { + throw new \InvalidArgumentException('Unknown attribute "' . $name . '"'); + } + + return $this->attributes[$name]; + } + + /** + * {@inheritDoc} + */ + public function hasAttribute(string $name): bool + { + return isset($this->attributes[$name]); + } + + /** + * {@inheritDoc} + */ + public function getAttributes(): array + { + return \array_values($this->attributes); + } + + /** + * {@inheritDoc} + */ + public function getAttributesOfType(string $typeName): \Traversable + { + foreach ($this->attributes as $attribute) { + if ($typeName === $attribute->getType()) { + yield $attribute; + } + } + } +} diff --git a/src/Information/MetaModelInformationInterface.php b/src/Information/MetaModelInformationInterface.php new file mode 100644 index 000000000..b20e2552e --- /dev/null +++ b/src/Information/MetaModelInformationInterface.php @@ -0,0 +1,105 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Information; + +/** + * This holds the schema information for a MetaModel. + */ +interface MetaModelInformationInterface +{ + /** + * Obtain the internal name of the attribute. + * + * @return string + */ + public function getName(): string; + + /** + * Obtain the configuration for the attribute. + * + * @return array + */ + public function getConfiguration(): array; + + /** + * Check if a configuration value exists. + * + * @param string $name The name of the configuration value to test. + * + * @return bool + */ + public function hasConfigurationValue(string $name): bool; + + /** + * Obtain a single configuration value. + * + * @param string $name The name of the configuration value to obtain. + * + * @return mixed + * + * @throws \RuntimeException When the configuration key does not exist. + */ + public function getConfigurationValue(string $name); + + /** + * Obtain the attribute names. + * + * @return string[] + */ + public function getAttributeNames(): array; + + /** + * Retrieve attributes. + * + * @param string $name The name of the attribute. + * + * @return AttributeInformationInterface + * + * @throws \InvalidArgumentException When the attribute is not registered. + */ + public function getAttribute(string $name): AttributeInformationInterface; + + /** + * Test if an attribute has been registered. + * + * @param string $name The name of the attribute to test. + * + * @return bool + */ + public function hasAttribute(string $name): bool; + + /** + * Retrieve attributes. + * + * @return list + */ + public function getAttributes(): array; + + /** + * Retrieve attributes. + * + * @param string $typeName Retrieve all attributes of a certain type. + * + * @return \Traversable + */ + public function getAttributesOfType(string $typeName): \Traversable; +} diff --git a/src/InformationProvider/ContaoDatabaseBackedInformationProvider.php b/src/InformationProvider/ContaoDatabaseBackedInformationProvider.php new file mode 100644 index 000000000..d74114500 --- /dev/null +++ b/src/InformationProvider/ContaoDatabaseBackedInformationProvider.php @@ -0,0 +1,119 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\InformationProvider; + +use Doctrine\DBAL\Connection; +use MetaModels\Information\AttributeInformation; +use MetaModels\Information\MetaModelInformation; + +/** + * This adds information from the tl_metamodel_* tables in Contao. + */ +class ContaoDatabaseBackedInformationProvider implements InformationProviderInterface +{ + /** + * The database connection. + * + * @var Connection + */ + private $connection; + + /** + * Create a new instance. + * + * @param Connection $connection + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * {@inheritDoc} + */ + public function getNames(): array + { + if ($this->connection->createSchemaManager()->tablesExist('tl_metamodel')) { + return $this + ->connection + ->createQueryBuilder() + ->select('tableName') + ->from('tl_metamodel') + ->executeQuery() + ->fetchFirstColumn(); + } + + return []; + } + + /** + * {@inheritDoc} + * + * @throws \RuntimeException On error. + */ + public function getInformationFor(MetaModelInformation $information): void + { + $configuration = $this + ->connection + ->createQueryBuilder() + ->select('*') + ->from('tl_metamodel') + ->where('tableName=:tableName') + ->setParameter('tableName', $information->getName()) + ->setMaxResults(1) + ->executeQuery() + ->fetchAssociative(); + + // Not managed by us. + if (false === $configuration) { + return; + } + + $information->addConfiguration($configuration); + + // Now add the attributes. + $attributeRows = $this + ->connection + ->createQueryBuilder() + ->select('*') + ->from('tl_metamodel_attribute') + ->where('pid=:pid') + ->setParameter('pid', $information->getConfigurationValue('id')) + ->orderBy('sorting') + ->executeQuery() + ->fetchAllAssociative(); + + foreach ($attributeRows as $attributeRow) { + $colName = $attributeRow['colname']; + if (!$information->hasAttribute($colName)) { + $information->addAttribute(new AttributeInformation($colName, $attributeRow['type'], $attributeRow)); + continue; + } + + $attribute = $information->getAttribute($colName); + if ($attribute instanceof AttributeInformation) { + $attribute->addConfiguration($attributeRow); + } + } + } +} diff --git a/src/InformationProvider/InformationProviderInterface.php b/src/InformationProvider/InformationProviderInterface.php new file mode 100644 index 000000000..0ec89f1f1 --- /dev/null +++ b/src/InformationProvider/InformationProviderInterface.php @@ -0,0 +1,49 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\InformationProvider; + +use MetaModels\Information\MetaModelInformation; + +/** + * This collects information for MetaModels. + */ +interface InformationProviderInterface +{ + /** + * Gets the names of known MetaModels. + * + * @return list + */ + public function getNames(): array; + + /** + * Obtain the schema for a single MetaModel. + * + * If the provider does not know the MetaModel, it must ignore it. + * + * @param MetaModelInformation $information The information to which to add. + * + * @return void + */ + public function getInformationFor(MetaModelInformation $information): void; +} diff --git a/src/InformationProvider/MetaModelInformationCollector.php b/src/InformationProvider/MetaModelInformationCollector.php new file mode 100644 index 000000000..02e8c9cb8 --- /dev/null +++ b/src/InformationProvider/MetaModelInformationCollector.php @@ -0,0 +1,91 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\InformationProvider; + +use MetaModels\Information\MetaModelCollection; +use MetaModels\Information\MetaModelCollectionInterface; +use MetaModels\Information\MetaModelInformation; + +/** + * This collects information for MetaModels. + */ +class MetaModelInformationCollector implements InformationProviderInterface +{ + /** + * The schema collectors to use. + * + * @var InformationProviderInterface[] + */ + private array $providers; + + /** + * Create a new instance. + * + * @param InformationProviderInterface[] $providers + */ + public function __construct(array $providers) + { + $this->providers = $providers; + } + + /** + * {@inheritDoc} + */ + public function getNames(): array + { + $nameLists = \array_values(\array_map( + /** @return list */ + static fn (InformationProviderInterface $provider): array => $provider->getNames(), + $this->providers + )); + + return \array_values(\array_unique(\array_merge(...$nameLists))); + } + + /** + * {@inheritDoc} + */ + public function getInformationFor(MetaModelInformation $information): void + { + foreach ($this->providers as $provider) { + $provider->getInformationFor($information); + } + } + + /** + * Obtain all MetaModel information. + * + * @return MetaModelCollectionInterface + */ + public function getCollection(): MetaModelCollectionInterface + { + $collection = new MetaModelCollection(); + + foreach ($this->getNames() as $name) { + $collection->add($metaModel = new MetaModelInformation($name)); + $this->getInformationFor($metaModel); + } + + return $collection; + } +} diff --git a/src/InsertTag/Node.php b/src/InsertTag/Node.php index 2db635706..0f674db76 100644 --- a/src/InsertTag/Node.php +++ b/src/InsertTag/Node.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2023 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/core * @author Christian Schiffler - * @copyright 2012-2023 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -34,10 +35,10 @@ final class Node implements IteratorAggregate, NodeInterface /** @var list */ private array $parts; - /** @param list ...$parts */ + /** @param NodeInterface ...$parts */ public function __construct(NodeInterface ...$parts) { - $this->parts = $parts; + $this->parts = \array_values($parts); } /** @@ -55,7 +56,7 @@ public function getIterator(): Traversable */ public function asString(): string { - return '{{' . array_reduce( + return '{{' . \array_reduce( $this->parts, fn($result, $arg) => $result . $arg->asString(), '' diff --git a/src/InsertTag/NodeList.php b/src/InsertTag/NodeList.php index 8874fea64..260a7154e 100644 --- a/src/InsertTag/NodeList.php +++ b/src/InsertTag/NodeList.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2023 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,7 @@ * * @package MetaModels/core * @author Christian Schiffler - * @copyright 2012-2023 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -45,7 +45,7 @@ final class NodeList implements IteratorAggregate, NodeInterface */ public function __construct(NodeInterface ...$elements) { - $this->elements = $elements; + $this->elements = \array_values($elements); } /** diff --git a/src/InsertTag/Parser.php b/src/InsertTag/Parser.php index b4df94296..cf5fb30a1 100644 --- a/src/InsertTag/Parser.php +++ b/src/InsertTag/Parser.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2023 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -12,7 +12,8 @@ * * @package MetaModels/core * @author Christian Schiffler - * @copyright 2012-2023 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,7 +24,6 @@ use LogicException; -use function preg_match; use function strlen; use function strpos; use function substr; @@ -77,9 +77,7 @@ private function __construct(string $content) */ public static function parse(string $content): NodeList { - $instance = new self($content); - - return $instance->parseInternal(); + return (new self($content))->parseInternal(); } /** @@ -93,13 +91,11 @@ private function parseInternal(): NodeList { $buffer = []; - while (null !== $nextOpening = $this->findNextOpenTagPosition()) { - if (null !== $nextOpening) { - if ($nextOpening > $this->position) { - $buffer[] = new LiteralNode($this->read($nextOpening - $this->position)); - } - $buffer[] = $this->readTag(); + while (null !== ($nextOpening = $this->findNextOpenTagPosition())) { + if ($nextOpening > $this->position) { + $buffer[] = new LiteralNode($this->read($nextOpening - $this->position)); } + $buffer[] = $this->readTag(); } if ($this->position < $this->length) { @@ -122,6 +118,7 @@ private function readTag(): Node if (!$this->isAtOpening()) { throw new LogicException('Expected to be at opening of insert tag.'); } + $this->read(2); // Read anything up to the next open or close tag. $parts = []; @@ -138,11 +135,12 @@ private function readTag(): Node // Tag is closed, end here. if ($this->isAtClosing()) { $this->read(2); + return new Node(...$parts); } // 2. a close tag => this tag is complete. - if ($nextClosing > $this->position) { + if ((null !== $nextClosing) && $nextClosing > $this->position) { $parts[] = new LiteralNode($this->read($nextClosing - $this->position)); } @@ -192,6 +190,7 @@ private function findNextOpenTagPosition(): ?int if (false === $next = strpos($this->content, '{{', $this->position)) { return null; } + return $next; } @@ -205,6 +204,7 @@ private function findNextCloseTagPosition(): ?int if (false === $next = strpos($this->content, '}}', $this->position)) { return null; } + return $next; } diff --git a/src/Item.php b/src/Item.php index 067444a62..44d502e24 100644 --- a/src/Item.php +++ b/src/Item.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -19,7 +19,7 @@ * @author Richard Henkenjohann * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -38,13 +38,14 @@ * Interface for a MetaModel item. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Item implements IItem { /** * The MetaModel instance attached to the item. * - * Get's populated with the first call to getMetaModel() (lazy initialization). + * Gets populated with the first call to getMetaModel() (lazy initialization). * * @var IMetaModel */ @@ -55,14 +56,14 @@ class Item implements IItem * * @var array */ - protected $arrData = array(); + protected $arrData = []; /** * The event dispatcher. * * @var null|EventDispatcherInterface */ - private $dispatcher; + private ?EventDispatcherInterface $dispatcher; /** * Create a new instance. @@ -73,7 +74,6 @@ class Item implements IItem */ public function __construct(IMetaModel $objMetaModel, $arrData, EventDispatcherInterface $dispatcher = null) { - $this->arrData = $arrData; $this->metaModel = $objMetaModel; $this->dispatcher = $dispatcher; @@ -104,6 +104,8 @@ public function __construct(IMetaModel $objMetaModel, $arrData, EventDispatcherI * Retrieve the service container. * * @return IMetaModelsServiceContainer + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer() { @@ -113,6 +115,7 @@ public function getServiceContainer() E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedMethod */ return $this->getMetaModel()->getServiceContainer(); } @@ -127,6 +130,7 @@ protected function getEventDispatcher() return $this->dispatcher; } + /** @psalm-suppress DeprecatedMethod */ return $this->getServiceContainer()->getEventDispatcher(); } @@ -134,9 +138,7 @@ protected function getEventDispatcher() * Helper function for {@see MetaModelItem::parseValue()} and {@see MetaModelItem::parseAttribute()}. * * @param IAttribute $objAttribute The attribute to parse. - * * @param string $strOutputFormat The desired output format. - * * @param ICollection|null $objSettings The settings object to be applied. * * @return array The parsed information for the given attribute. @@ -144,30 +146,32 @@ protected function getEventDispatcher() public function internalParseAttribute($objAttribute, $strOutputFormat, $objSettings) { if ($objAttribute instanceof IInternal) { - return array(); + return []; } - $arrResult = array(); + $arrResult = []; - if ($objAttribute) { - // Extract view settings for this attribute. - if ($objSettings) { - $objAttributeSettings = $objSettings->getSetting($objAttribute->getColName()); - } else { - $objAttributeSettings = null; - } - foreach ($objAttribute->parseValue( + // Extract view settings for this attribute. + if ($objSettings) { + $objAttributeSettings = $objSettings->getSetting($objAttribute->getColName()); + } else { + $objAttributeSettings = null; + } + + foreach ( + $objAttribute->parseValue( $this->arrData, $strOutputFormat, $objAttributeSettings - ) as $strKey => $varValue) { - $arrResult[$strKey] = $varValue; - } + ) as $strKey => $varValue + ) { + $arrResult[$strKey] = $varValue; } // If "hideEmptyValues" is true and the raw is empty remove text and output format. - if (($objSettings instanceof ICollection) - && $objSettings->get('hideEmptyValues') + if ( + ($objSettings instanceof ICollection) + && (bool) $objSettings->get('hideEmptyValues') && EmptyTest::isEmptyValue($arrResult['raw']) ) { unset($arrResult[$strOutputFormat]); @@ -215,7 +219,8 @@ protected function isArrayEmpty($arrArray) E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - if (!is_array($arrArray)) { + /** @psalm-suppress DocblockTypeContradiction */ + if (!\is_array($arrArray)) { // This should never happen but was possible before. return EmptyTest::isEmptyValue($arrArray); } @@ -232,14 +237,13 @@ protected function isArrayEmpty($arrArray) */ public function get($strAttributeName) { - return array_key_exists($strAttributeName, $this->arrData) ? $this->arrData[$strAttributeName] : null; + return \array_key_exists($strAttributeName, $this->arrData) ? $this->arrData[$strAttributeName] : null; } /** * Set the native value of an Attribute. * * @param string $strAttributeName The name of the attribute. - * * @param mixed $varValue The value of the attribute. * * @return IItem @@ -266,7 +270,7 @@ public function getMetaModel() * * @param string $strAttributeName The name of the attribute. * - * @return IAttribute The instance. + * @return null|IAttribute the instance or null if not found. */ public function getAttribute($strAttributeName) { @@ -274,7 +278,7 @@ public function getAttribute($strAttributeName) } /** - * Check if the given attribute is set. This mean if in the data array + * Check if the given attribute is set. This was mean if in the data array * is the filed set or not. If the attribute is not loaded the function * will return false. * @@ -285,7 +289,7 @@ public function getAttribute($strAttributeName) */ public function isAttributeSet($strAttributeName) { - return array_key_exists($strAttributeName, $this->arrData); + return \array_key_exists($strAttributeName, $this->arrData); } /** @@ -296,13 +300,13 @@ public function isAttributeSet($strAttributeName) */ public function getSetAttributes() { - return array_keys($this->arrData); + return \array_keys($this->arrData); } /** * Determines if this item is a variant of another item. * - * @return bool True if it is an variant, false otherwise. + * @return bool True if it is a variant, false otherwise. */ public function isVariant() { @@ -316,7 +320,7 @@ public function isVariant() * this item. It merely simply states, that this item is able * to function as variant base for other items. * - * @return bool True if it is an variant base, false otherwise. + * @return bool True if it is a variant base, false otherwise. */ public function isVariantBase() { @@ -324,7 +328,7 @@ public function isVariantBase() } /** - * Fetch the meta model variants for this item. + * Fetch the MetaModel variants for this item. * * @param IFilter $objFilter The filter settings to be applied. * @@ -333,14 +337,14 @@ public function isVariantBase() public function getVariants($objFilter) { if ($this->isVariantBase()) { - return $this->getMetaModel()->findVariants(array($this->get('id')), $objFilter); + return $this->getMetaModel()->findVariants([$this->get('id')], $objFilter); } return null; } /** - * Fetch the meta model variant base for this item. + * Fetch the MetaModel variant base for this item. * * Note: For a non-variant item the variant base is the item itself. * @@ -349,7 +353,12 @@ public function getVariants($objFilter) public function getVariantBase() { if ($this->getMetaModel()->hasVariants() && !$this->isVariantBase()) { - return $this->getMetaModel()->findById($this->get('vargroup')); + $base = $this->getMetaModel()->findById($this->get('vargroup')); + if (null === $base) { + throw new \RuntimeException('Database corruption, missing base item for variant.'); + } + + return $base; } return $this; @@ -369,7 +378,8 @@ public function getSiblings($objFilter) if (!$this->getMetaModel()->hasVariants()) { return null; } - return $this->getMetaModel()->findVariantsWithBase(array($this->get('id')), $objFilter); + + return $this->getMetaModel()->findVariantsWithBase([$this->get('id')], $objFilter); } /** @@ -414,7 +424,7 @@ protected function registerAssets($objSettings) $arrCss = $objSettings->get('additionalCss'); foreach ((array) $arrCss as $arrFile) { - if ($arrFile['published']) { + if (isset($arrFile['published']) && $arrFile['published']) { $GLOBALS['TL_CSS'][md5($arrFile['file'])] = $arrFile['file']; } } @@ -423,7 +433,7 @@ protected function registerAssets($objSettings) $arrJs = $objSettings->get('additionalJs'); foreach ((array) $arrJs as $arrFile) { - if ($arrFile['published']) { + if (isset($arrFile['published']) && $arrFile['published']) { $GLOBALS['TL_JAVASCRIPT'][md5($arrFile['file'])] = $arrFile['file']; } } @@ -433,10 +443,11 @@ protected function registerAssets($objSettings) * Renders the item in the given output format. * * @param string $strOutputFormat The desired output format (optional - default: text). - * * @param ICollection $objSettings The render settings to use (optional - default: null). * * @return array attribute name => format => value + * + * @psalm-suppress InvalidArrayOffset */ public function parseValue($strOutputFormat = 'text', $objSettings = null) { @@ -462,18 +473,19 @@ public function parseValue($strOutputFormat = 'text', $objSettings = null) return $arrResult; } - // Add jumpTo link + // Add jumpTo link. $jumpTo = $this->buildJumpToLink($objSettings); - if ($jumpTo['url']) { + if ('' !== ($jumpTo['url'] ?? '')) { $arrResult['actions']['jumpTo'] = [ 'href' => $jumpTo['url'], 'deep' => $jumpTo['deep'], - 'label' => $this->getCaptionText('details'), + 'label' => $jumpTo['label'], 'class' => 'details' ]; } // Just here for backwards compatibility with templates. See #1087 + // @deprecated usage of array key 'jumpTo' - remove in MM 3.0. $arrResult['jumpTo'] = $jumpTo; // First, parse the values in the same order as they are in the render settings. @@ -481,11 +493,13 @@ public function parseValue($strOutputFormat = 'text', $objSettings = null) $objAttribute = $this->getMetaModel()->getAttribute($strAttrName); if ($objAttribute) { $arrResult['attributes'][$objAttribute->getColName()] = $objAttribute->getName(); - foreach ($this->internalParseAttribute( - $objAttribute, - $strOutputFormat, - $objSettings - ) as $strKey => $varValue) { + foreach ( + $this->internalParseAttribute( + $objAttribute, + $strOutputFormat, + $objSettings + ) as $strKey => $varValue + ) { $arrResult[$strKey][$objAttribute->getColName()] = $varValue; } } @@ -508,7 +522,7 @@ public function parseValue($strOutputFormat = 'text', $objSettings = null) * * The returning array will hold the following keys: * * params - the url parameter (only if a valid filter setting could be determined). - * * deep - boolean true, if parameters are non empty, false otherwise. + * * deep - boolean true, if parameters are non-empty, false otherwise. * * page - id of the jumpTo page. * * url - the complete generated url * @@ -518,10 +532,6 @@ public function parseValue($strOutputFormat = 'text', $objSettings = null) */ public function buildJumpToLink($objSettings) { - if (!$objSettings) { - return null; - } - return $objSettings->buildJumpToUrlFor($this); } @@ -529,16 +539,19 @@ public function buildJumpToLink($objSettings) * Renders a single attribute in the given output format. * * @param string $strAttributeName The desired attribute. - * * @param string $strOutputFormat The desired output format (optional - default: text). - * * @param ICollection $objSettings The render settings to use (optional - default: null). * * @return array format=>value */ public function parseAttribute($strAttributeName, $strOutputFormat = 'text', $objSettings = null) { - return $this->internalParseAttribute($this->getAttribute($strAttributeName), $strOutputFormat, $objSettings); + $attribute = $this->getAttribute($strAttributeName); + if (!$attribute instanceof IAttribute) { + return []; + } + + return $this->internalParseAttribute($attribute, $strOutputFormat, $objSettings); } /** @@ -555,6 +568,7 @@ public function copy() unset($arrNewData['id']); unset($arrNewData['tstamp']); unset($arrNewData['vargroup']); + return new Item($this->getMetaModel(), $arrNewData, $this->dispatcher); } @@ -564,7 +578,7 @@ public function copy() * Additionally, the item will be a variant child of this item. * * NOTE: if this item is not a variant base itself, this item will return a item - * that is a child of this items variant base. i.e. exact clone. + * that is a child of these items variant base. i.e. exact clone. * * @return \MetaModels\IItem the new copy. */ @@ -579,40 +593,8 @@ public function varCopy() $objNewItem->set('vargroup', $this->get('vargroup')); $objNewItem->set('varbase', '0'); } - return $objNewItem; - } - - /** - * Retrieve the translation string for the given lang key. - * - * In order to achieve the correct caption text, the function tries several translation strings sequentially. - * The first language key that is set will win, even if it is to be considered empty. - * - * This message is looked up in the following order: - * 1. $GLOBALS['TL_LANG']['MSC'][][][$langKey] - * 2. $GLOBALS['TL_LANG']['MSC'][][$langKey] - * 3. $GLOBALS['TL_LANG']['MSC'][$langKey] - * - * @param string $langKey The language key to retrieve. - * - * @return string - * - * @SuppressWarnings(PHPMD.Superglobals) - * @SuppressWarnings(PHPMD.CamelCaseVariableName) - */ - private function getCaptionText($langKey) - { - $tableName = $this->getMetaModel()->getTableName(); - if (isset($this->objView) - && isset($GLOBALS['TL_LANG']['MSC'][$tableName][$this->objView->get('id')][$langKey]) - ) { - return $GLOBALS['TL_LANG']['MSC'][$tableName][$this->objView->get('id')][$langKey]; - } elseif (isset($GLOBALS['TL_LANG']['MSC'][$tableName][$langKey])) { - return $GLOBALS['TL_LANG']['MSC'][$tableName][$langKey]; - } - - return $GLOBALS['TL_LANG']['MSC'][$langKey]; + return $objNewItem; } /** @@ -628,7 +610,7 @@ private function variantCssClass() if ($this->isVariantBase()) { $result = 'varbase'; - if (0 !== $this->getVariants(null)->getCount()) { + if (0 !== ($this->getVariants($this->getMetaModel()->getEmptyFilter())?->getCount() ?? 0)) { $result .= ' varbase-with-variants'; } return $result; diff --git a/src/ItemList.php b/src/ItemList.php index 8a5bc91c9..a10de0b28 100644 --- a/src/ItemList.php +++ b/src/ItemList.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -25,7 +25,7 @@ * @author David Molineus * @author Ingolf Steinhardt * @author Fritz Michael Gschwantner - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -44,19 +44,35 @@ use MetaModels\Filter\IFilterRule; use MetaModels\Filter\Setting\ICollection as IFilterSettingCollection; use MetaModels\Filter\Setting\IFilterSettingFactory; +use MetaModels\Helper\LocaleUtil; use MetaModels\Helper\PaginationLimitCalculator; +use MetaModels\Helper\SortingLinkGenerator; use MetaModels\Render\Setting\ICollection as IRenderSettingCollection; use MetaModels\Render\Setting\IRenderSettingFactory; use MetaModels\Render\Template; use RuntimeException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\LegacyEventDispatcherProxy; +use Symfony\Component\HttpFoundation\Request; +use Symfony\Contracts\Translation\TranslatorInterface; + +use function array_key_exists; +use function array_keys; +use function array_merge; +use function func_num_args; +use function in_array; +use function is_object; +use function sprintf; +use function strtoupper; +use function trigger_error; /** * Implementation of a general purpose MetaModel listing. * * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) * @SuppressWarnings(PHPMD.ExcessiveClassLength) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * @SuppressWarnings(PHPMD.TooManyFields) + * @SuppressWarnings(PHPMD.LongVariable) */ class ItemList { @@ -77,35 +93,37 @@ class ItemList /** * The view id to use. * - * @var int + * @var string */ - protected int $intView = 0; + protected string $intView = '0'; /** * Output format type. * * @var string|null */ - protected ?string $outputFormat; + protected ?string $outputFormat = null; /** * The MetaModel id to use. * - * @var int + * @var string */ - protected int $intMetaModel = 0; + protected string $intMetaModel = '0'; /** * The filter id to use. * - * @var int + * @var string + * + * @deprecated Not in use anymore - remove in MetaModels 3.0 */ - protected int $intFilter = 0; + protected string $intFilter = '0'; /** * The parameters for the filter. * - * @var string[] + * @var array|string> */ protected array $arrParam = []; @@ -163,7 +181,21 @@ class ItemList * * @var EventDispatcherInterface|null */ - private $eventDispatcher; + private ?EventDispatcherInterface $eventDispatcher; + + /** + * The language. + * + * @var string|null + */ + private ?string $language = null; + + /** + * The sorting link generator. + * + * @var SortingLinkGenerator|null + */ + private ?SortingLinkGenerator $sortingLinkGenerator = null; /** * Create a new instance. @@ -207,7 +239,7 @@ public function __construct( $this->factory = $factory; $this->filterFactory = $filterFactory; $this->renderSettingFactory = $renderSettingFactory; - $this->eventDispatcher = LegacyEventDispatcherProxy::decorate($eventDispatcher); + $this->eventDispatcher = $eventDispatcher; } /** @@ -219,7 +251,7 @@ public function __construct( * * @deprecated Use constructor injection instead. */ - public function setFilterFactory(IFilterSettingFactory $filterFactory) + public function setFilterFactory(IFilterSettingFactory $filterFactory): self { // @codingStandardsIgnoreStart @trigger_error( @@ -239,7 +271,7 @@ public function setFilterFactory(IFilterSettingFactory $filterFactory) */ private function getFilterFactory(): IFilterSettingFactory { - if ($this->filterFactory) { + if (null !== $this->filterFactory) { return $this->filterFactory; } // @codingStandardsIgnoreStart @@ -249,8 +281,10 @@ private function getFilterFactory(): IFilterSettingFactory E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + $factory = System::getContainer()->get('metamodels.filter_setting_factory'); + assert($factory instanceof IFilterSettingFactory); - return $this->filterFactory = System::getContainer()->get('metamodels.filter_setting_factory'); + return $this->filterFactory = $factory; } /** @@ -292,8 +326,10 @@ private function getFactory(): IFactory E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + $factory = System::getContainer()->get('metamodels.factory'); + assert($factory instanceof IFactory); - return $this->factory = System::getContainer()->get('metamodels.factory'); + return $this->factory = $factory; } /** @@ -335,7 +371,7 @@ public function setEventDispatcher(EventDispatcherInterface $eventDispatcher): s E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $this->eventDispatcher = LegacyEventDispatcherProxy::decorate($eventDispatcher); + $this->eventDispatcher = $eventDispatcher; return $this; } @@ -358,10 +394,10 @@ private function getEventDispatcher(): EventDispatcherInterface E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); - return $this->eventDispatcher = LegacyEventDispatcherProxy::decorate( - System::getContainer()->get('event_dispatcher') - ); + return $this->eventDispatcher = $dispatcher; } /** @@ -394,6 +430,8 @@ public function setServiceContainerFallback(): self * @return IMetaModelsServiceContainer|null * * @deprecated The service container will get removed, use the symfony service container instead. + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer(): ?IMetaModelsServiceContainer { @@ -404,9 +442,7 @@ public function getServiceContainer(): ?IMetaModelsServiceContainer * Set the limit. * * @param bool $isLimit If true, use limit, if false no limit is applied. - * * @param int $offset Like in SQL, first element to be returned (0 based). - * * @param int $limit Like in SQL, amount of elements to retrieve. * * @return ItemList @@ -440,7 +476,6 @@ public function setPageBreak(int $limit): self * Set sorting to an attribute or system column optionally in the given direction. * * @param string $sortBy The name of the attribute or system column to be used for sorting. - * * @param string $sortDirection The direction, either ASC or DESC (optional). * * @return ItemList @@ -466,7 +501,7 @@ public function overrideOutputFormat(string $outputFormat = null): self if ('' !== $outputFormat) { $this->outputFormat = $outputFormat; } else { - unset($this->outputFormat); + $this->outputFormat = null; } return $this; @@ -475,13 +510,12 @@ public function overrideOutputFormat(string $outputFormat = null): self /** * Set MetaModel and render settings. * - * @param int $intMetaModel The MetaModel to use. - * - * @param int $intView The render settings to use (if 0, the default will be used). + * @param string $intMetaModel The MetaModel to use. + * @param string $intView The render settings to use (if 0, the default will be used). * * @return ItemList */ - public function setMetaModel(int $intMetaModel, int $intView): self + public function setMetaModel(string $intMetaModel, string $intView): self { $this->intMetaModel = $intMetaModel; $this->intView = $intView; @@ -497,7 +531,6 @@ public function setMetaModel(int $intMetaModel, int $intView): self * Add the attribute names for meta title and description. * * @param string $titleAttribute Name of attribute for title. - * * @param string $descriptionAttribute Name of attribute for description. * * @return ItemList @@ -510,47 +543,61 @@ public function setMetaTags(string $titleAttribute, string $descriptionAttribute return $this; } + /** + * Set the language. + * + * @param string $language The language. + * + * @return $this + */ + public function setLanguage(string $language): self + { + $this->language = $language; + + return $this; + } + /** * The Metamodel to use. * - * @var IMetaModel + * @var IMetaModel|null */ - protected $objMetaModel; + protected $objMetaModel = null; /** * The render settings to use. * - * @var IRenderSettingCollection + * @var IRenderSettingCollection|null */ - protected $objView; + protected $objView = null; /** * The render template to use (metamodel_). * - * @var Template + * @var Template|null */ - protected $objTemplate; + protected $objTemplate = null; /** * The list template (ce_ or mod_). * - * @var ContaoTemplate + * @var ContaoTemplate|null */ - private $listTemplate; + private $listTemplate = null; /** * The filter settings to use. * - * @var IFilterSettingCollection + * @var IFilterSettingCollection|null */ - protected $objFilterSettings; + protected $objFilterSettings = null; /** * The filter to use. * - * @var IFilter + * @var IFilter|null */ - protected $objFilter; + protected $objFilter = null; /** * The model, can be module model or content model. @@ -559,7 +606,7 @@ public function setMetaTags(string $titleAttribute, string $descriptionAttribute * * @deprecated Do not use. */ - private $model; + private ModuleModel|null|ContentModel $model = null; /** * Get the model. @@ -570,6 +617,7 @@ public function setMetaTags(string $titleAttribute, string $descriptionAttribute */ public function getModel(): ?Model { + /** @psalm-suppress DeprecatedProperty */ return $this->model; } @@ -579,6 +627,8 @@ public function getModel(): ?Model * @return void * * @throws RuntimeException When the MetaModel can not be found. + * + * @psalm-assert IMetaModel $this->objMetaModel */ protected function prepareMetaModel(): void { @@ -598,47 +648,40 @@ protected function prepareMetaModel(): void */ protected function prepareView(): void { + $metaModel = $this->getMetaModel(); if ($this->renderSettingFactory) { - $this->objView = $this->renderSettingFactory->createCollection($this->objMetaModel, $this->intView); + $this->objView = $this->renderSettingFactory->createCollection($metaModel, $this->intView); } else { - $this->objView = $this->objMetaModel->getView($this->intView); + /** @psalm-suppress DeprecatedMethod */ + $this->objView = $metaModel->getView((int) $this->intView); } - if ($this->objView) { - $this->objTemplate = new Template($this->objView->get('template')); - $this->objTemplate->view = $this->objView; - } else { - // Fallback to default. - $this->objTemplate = new Template('metamodel_full'); - } + $this->objTemplate = new Template((string) $this->objView->get('template')); + $this->objTemplate->view = $this->objView; } /** * Set the filter setting to use. * - * @param int $intFilter The filter setting id to use. + * @param string $intFilter The filter setting id to use. * * @return $this * * @throws RuntimeException When the filter settings can not be found. */ - public function setFilterSettings(int $intFilter): self + public function setFilterSettings(string $intFilter): self { $this->objFilterSettings = $this->getFilterFactory()->createCollection($intFilter); - if (!$this->objFilterSettings) { - throw new RuntimeException('Error: no filter object defined.'); - } - return $this; } /** * Set parameters. * - * @param string[][] $presets The parameter preset values to use. - * - * @param string[] $values The dynamic parameter values that may be used. + * @param array $presets The parameter preset values to use. + * @param array|string> $values The dynamic parameter values that may be + * used. * * @return ItemList * @@ -673,8 +716,8 @@ public function setFilterParameters(array $presets, array $values): self continue; } - // Not a preset or allowed to overriding? - use value. - if ((!array_key_exists($filterParameterKey, $presets)) || $presets[$filterParameterKey]['use_get']) { + // Not a preset or allowed to override? - use value. + if ((!array_key_exists($filterParameterKey, $presets)) || (bool) $presets[$filterParameterKey]['use_get']) { $processed[$filterParameterKey] = $values[$filterParameterKey]; } } @@ -691,6 +734,10 @@ public function setFilterParameters(array $presets, array $values): self */ public function getFilter(): IFilter { + if (null === $this->objFilter) { + throw new RuntimeException('No filter is set.'); + } + return $this->objFilter; } @@ -701,6 +748,12 @@ public function getFilter(): IFilter */ public function getFilterSettings(): IFilterSettingCollection { + if (null === $this->objFilterSettings) { + throw new RuntimeException( + 'Error: no filter object defined, call setFilterSettings() before.' + ); + } + return $this->objFilterSettings; } @@ -736,7 +789,7 @@ public function setListTemplate(ContaoTemplate $template): self * * @return void */ - public function setTemplateParameter(string $name, $value): void + public function setTemplateParameter(string $name, mixed $value): void { $this->templateParameter[$name] = $value; } @@ -744,9 +797,9 @@ public function setTemplateParameter(string $name, $value): void /** * The items in the list view. * - * @var IItems + * @var IItems|null */ - protected IItems $objItems; + protected ?IItems $objItems = null; /** * Add additional filter rules to the list. @@ -770,7 +823,7 @@ protected function modifyFilter(): self public function addFilterRule(IFilterRule $filterRule): self { if (!$this->objFilter) { - $this->objFilter = $this->objMetaModel->getEmptyFilter(); + $this->objFilter = $this->getMetaModel()->getEmptyFilter(); } $this->objFilter->addFilterRule($filterRule); @@ -783,32 +836,52 @@ public function addFilterRule(IFilterRule $filterRule): self * * In this base implementation, this only includes the attributes mentioned in the render setting. * - * @return string[] the names of the attributes to be fetched. + * @return list the names of the attributes to be fetched. */ protected function getAttributeNames(): array { - $attributes = $this->objView->getSettingNames(); - - // Get the right jumpTo. - $desiredLanguage = $this->getMetaModel()->getActiveLanguage(); - $fallbackLanguage = $this->getMetaModel()->getFallbackLanguage(); - - $filterSettingsId = 0; + $attributes = $this->getView()->getSettingNames(); + $metaModel = $this->getMetaModel(); + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ + if ($metaModel instanceof ITranslatedMetaModel) { + $desiredLanguage = $metaModel->getLanguage(); + $fallbackLanguage = $metaModel->getMainLanguage(); + $isTranslated = true; + } elseif ($metaModel->isTranslated(false)) { + // Get the right jumpTo. + /** @psalm-suppress DeprecatedMethod */ + $desiredLanguage = $metaModel->getActiveLanguage(); + /** @psalm-suppress DeprecatedMethod */ + $fallbackLanguage = $metaModel->getFallbackLanguage(); + $isTranslated = true; + } else { + $desiredLanguage = + $fallbackLanguage = System::getContainer()->get('request_stack')?->getCurrentRequest()?->getLocale(); + $isTranslated = false; + } + $filterSettingsId = ''; foreach ((array) $this->getView()->get('jumpTo') as $jumpTo) { + $langCode = (string) ($jumpTo['langcode'] ?? ''); // If either desired language or fallback, keep the result. - if (!$this->getMetaModel()->isTranslated() - || $jumpTo['langcode'] == $desiredLanguage - || $jumpTo['langcode'] == $fallbackLanguage) { - $filterSettingsId = $jumpTo['filter']; - // If the desired language, break. Otherwise try to get the desired one until all have been evaluated. - if ($desiredLanguage === $jumpTo['langcode']) { + /** @psalm-suppress DeprecatedMethod */ + if ( + $langCode === $desiredLanguage + || $langCode === $fallbackLanguage + || !$isTranslated + ) { + $filterSettingsId = (string) ($jumpTo['filter'] ?? ''); + // If the desired language, break. Otherwise, try to get the desired one until all have been evaluated. + if ($desiredLanguage === $langCode) { break; } } } - if ($filterSettingsId) { + if ('' !== $filterSettingsId) { $filterSettings = $this->getFilterFactory()->createCollection($filterSettingsId); $attributes = array_merge($filterSettings->getReferencedAttributes(), $attributes); } @@ -825,13 +898,14 @@ protected function getAttributeNames(): array */ public function prepare(): self { - if (isset($this->objItems)) { + if (null !== $this->objItems) { return $this; } + $metaModel = $this->getMetaModel(); // Create an empty filter object if not done before. if (!isset($this->objFilter)) { - $this->objFilter = $this->objMetaModel->getEmptyFilter(); + $this->objFilter = $metaModel->getEmptyFilter(); } if (isset($this->objFilterSettings)) { @@ -840,27 +914,43 @@ public function prepare(): self $this->modifyFilter(); - $total = $this->objMetaModel->getCount($this->objFilter); + $total = $metaModel->getCount($this->objFilter); $calculator = $this->paginationLimitCalculator; $calculator->setTotalAmount($total); - $this->objTemplate->total = $total; + if (null !== $this->objTemplate) { + $this->objTemplate->total = $total; + } - if ($this->objMetaModel instanceof TranslatedMetaModel) { - $previousLanguage = $this->objMetaModel->selectLanguage(\str_replace('-', '_', $GLOBALS['TL_LANGUAGE'])); + if ($metaModel instanceof TranslatedMetaModel) { + if (null === $this->language) { + // @codingStandardsIgnoreStart + @trigger_error( + sprintf( + 'Not setting a language code in "%s" is deprecated since MetaModels 2.3 and will fail in 3.0', + __CLASS__ + ), + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + $this->language = LocaleUtil::formatAsLocale($GLOBALS['TL_LANGUAGE'] ?? 'en'); + } + $previousLanguage = $metaModel->selectLanguage($this->language); } - $this->objItems = $this->objMetaModel->findByFilter( + $this->objItems = $metaModel->findByFilter( $this->objFilter, $this->strSortBy, - $calculator->getCalculatedOffset(), - $calculator->getCalculatedLimit(), + (int) $calculator->getCalculatedOffset(), + (int) $calculator->getCalculatedLimit(), $this->strSortDirection, $this->getAttributeNames() ); - if (isset($previousLanguage)) { - $this->objMetaModel->selectLanguage($previousLanguage); + if (isset($previousLanguage) && ($metaModel instanceof TranslatedMetaModel)) { + $metaModel->selectLanguage($previousLanguage); } return $this; @@ -885,6 +975,10 @@ public function getPagination(): string */ public function getItems(): IItems { + if (null === $this->objItems) { + throw new RuntimeException('Call prepare first'); + } + return $this->objItems; } @@ -903,6 +997,7 @@ public function getObjItems(): IItems E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + return $this->getItems(); } @@ -913,6 +1008,10 @@ public function getObjItems(): IItems */ public function getView(): IRenderSettingCollection { + if (!$this->objView) { + throw new RuntimeException('No render setting set - call prepareView() first.'); + } + return $this->objView; } @@ -923,6 +1022,10 @@ public function getView(): IRenderSettingCollection */ public function getMetaModel(): IMetaModel { + if (!$this->objMetaModel) { + throw new RuntimeException('No metamodel object set - call prepareMetaModel() first.'); + } + return $this->objMetaModel; } @@ -936,7 +1039,13 @@ public function getMetaModel(): IMetaModel */ private function getPage(): ?object { - return ('FE' === TL_MODE && is_object($GLOBALS['objPage'])) ? $GLOBALS['objPage'] : null; + $isFrontend = (bool) System::getContainer() + ->get('contao.routing.scope_matcher') + ?->isFrontendRequest( + System::getContainer()->get('request_stack')?->getCurrentRequest() ?? Request::create('') + ); + + return ($isFrontend && is_object($page = $GLOBALS['objPage'])) ? $page : null; } /** @@ -946,12 +1055,12 @@ private function getPage(): ?object */ public function getOutputFormat(): string { - if (isset($this->outputFormat)) { + if (null !== $this->outputFormat) { return $this->outputFormat; } - if (isset($this->objView) && $this->objView->get('format')) { - return $this->objView->get('format'); + if ('' !== ($format = (string) $this->objView?->get('format'))) { + return $format; } $page = $this->getPage(); @@ -980,15 +1089,25 @@ public function getOutputFormat(): string * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) + * @psalm-suppress PossiblyNullArrayOffset */ private function getCaptionText(string $langKey): string { $tableName = $this->getMetaModel()->getTableName(); - if (isset($this->objView, $GLOBALS['TL_LANG']['MSC'][$tableName][$this->objView->get('id')][$langKey])) { + if ( + null !== $this->objView + && isset($GLOBALS['TL_LANG']['MSC'][$tableName][$this->objView->get('id')][$langKey]) + ) { return $GLOBALS['TL_LANG']['MSC'][$tableName][$this->objView->get('id')][$langKey]; } + if (null !== ($caption = $GLOBALS['TL_LANG']['MSC'][$tableName][$langKey] ?? null)) { + return $caption; + } + + $translator = System::getContainer()->get('translator'); + assert($translator instanceof TranslatorInterface); - return ($GLOBALS['TL_LANG']['MSC'][$tableName][$langKey] ?? $GLOBALS['TL_LANG']['MSC'][$langKey]); + return $translator->trans($langKey, [], 'metamodels_list'); } /** @@ -1013,11 +1132,13 @@ protected function getNoItemsCaption(): string * Set the title and description in the page object. * * @return void + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) */ private function setTitleAndDescription(): void { $page = $this->getPage(); - if ($page && $this->objItems->getCount()) { + if ($page && null !== $this->objItems && $this->objItems->getCount()) { // Add title if needed. if (!empty($this->strTitleAttribute)) { while ($this->objItems->next()) { @@ -1059,35 +1180,57 @@ private function setTitleAndDescription(): void } } + + /** + * Setter for SortingLinkGenerator. + * + * @param SortingLinkGenerator $generator The link generator. + * + * @return $this + */ + public function setSortingLinkGenerator(SortingLinkGenerator $generator): self + { + $this->sortingLinkGenerator = $generator; + + return $this; + } + /** * Render the list view. * * @param bool $isNoNativeParsing Flag determining if the parsing shall be done internal or if the template - * will handle the parsing on it's own. + * will handle the parsing on its own. * @param object|null $caller The object calling us, might be a Module or ContentElement or anything * else. * * @return string + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ public function render(bool $isNoNativeParsing, object $caller = null): string { if (func_num_args() > 1) { trigger_error('Passing $objCaller as second argument is deprecated', E_USER_DEPRECATED); if ($caller instanceof ContentModel || $caller instanceof ModuleModel) { + /** @psalm-suppress DeprecatedProperty */ $this->model = $caller; } } + if (null === $this->objTemplate) { + return ''; + } + $event = new RenderItemListEvent($this, $this->objTemplate, $caller); $this->getEventDispatcher()->dispatch($event, MetaModelsEvents::RENDER_ITEM_LIST); - $this->objTemplate->noItemsMsg = $this->getNoItemsCaption(); $this->objTemplate->details = $this->getCaptionText('details'); $this->prepare(); $outputFormat = $this->getOutputFormat(); - if (!$isNoNativeParsing && $this->objItems->getCount()) { + if (!$isNoNativeParsing && null !== $this->objItems && $this->objItems->getCount()) { $this->objTemplate->data = $this->objItems->parseAll($outputFormat, $this->objView); } else { $this->objTemplate->data = []; @@ -1095,10 +1238,37 @@ public function render(bool $isNoNativeParsing, object $caller = null): string $this->setTitleAndDescription(); - $this->objTemplate->caller = $caller; - $this->objTemplate->items = $this->objItems; - $this->objTemplate->filterParams = $this->arrParam; - $this->objTemplate->parameter = $this->templateParameter; + $generateSortingLink = function (string $attributeName, string $type): array { + if (null === $this->sortingLinkGenerator) { + return []; + } + + $attribute = $this->getMetaModel()->getAttribute($attributeName); + + if (null === $attribute) { + throw new RuntimeException('Attribute not found: ' . $attributeName); + } + + return $this->sortingLinkGenerator->generateSortingLink($attribute, $type); + }; + + $renderSortingLink = static function (string $attributeName, string $type) use ($generateSortingLink): string { + if ([] === $sortingLink = $generateSortingLink($attributeName, $type)) { + return ''; + } + + return <<{$sortingLink['label']} + EOF; + }; + + $this->objTemplate->generateSortingLink = $generateSortingLink; + $this->objTemplate->renderSortingLink = $renderSortingLink; + $this->objTemplate->caller = $caller; + $this->objTemplate->items = $this->objItems; + $this->objTemplate->filterParams = $this->arrParam; + $this->objTemplate->parameter = $this->templateParameter; return $this->objTemplate->parse($outputFormat); } diff --git a/src/Items.php b/src/Items.php index c2d986e99..15a4f85c0 100644 --- a/src/Items.php +++ b/src/Items.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,8 @@ * @author David Maack * @author Richard Henkenjohann * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -41,7 +42,7 @@ class Items implements IItems * * @var array */ - protected $arrItems = array(); + protected $arrItems = []; /** * Creates a new instance with the passed items. @@ -56,6 +57,7 @@ public function __construct($arrItems) /** * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function rewind() { $this->first(); @@ -64,6 +66,7 @@ public function rewind() /** * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function current() { return $this->getItem(); @@ -72,6 +75,7 @@ public function current() /** * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function key() { return $this->intCursor; @@ -80,6 +84,7 @@ public function key() /** * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function valid() { return ($this->offsetExists($this->intCursor)); @@ -88,17 +93,16 @@ public function valid() /** * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function offsetExists($offset) { - if (!is_numeric($offset)) { - return false; - } return (($this->getCount() > $offset) && ($offset > -1)); } /** * {@inheritDoc} */ + #[\ReturnTypeWillChange] public function offsetGet($offset) { if ($this->offsetExists($offset)) { @@ -112,7 +116,6 @@ public function offsetGet($offset) * Not implemented in this class. * * @param mixed $offset The offset to assign the value to. - * * @param mixed $value The value to set. * * @return void @@ -121,6 +124,7 @@ public function offsetGet($offset) * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ + #[\ReturnTypeWillChange] public function offsetSet($offset, $value) { throw new \RuntimeException('MetaModelItems is a read only class, you can not manipulate the collection.', 1); @@ -137,6 +141,7 @@ public function offsetSet($offset, $value) * * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ + #[\ReturnTypeWillChange] public function offsetUnset($offset) { throw new \RuntimeException('MetaModelItems is a read only class, you can not manipulate the collection.', 1); @@ -165,7 +170,7 @@ public function getItem() */ public function getCount() { - return count($this->arrItems); + return \count($this->arrItems); } /** @@ -177,22 +182,29 @@ public function first() $this->intCursor = 0; return $this; } + return false; } /** * {@inheritdoc} + * + * @psalm-suppress InvalidReturnType + * @psalm-suppress InvalidReturnStatement + * @psalm-suppress InvalidFalsableReturnType */ + #[\ReturnTypeWillChange] public function next() { - if ($this->getCount() == $this->intCursor) { + if ($this->getCount() === $this->intCursor) { return false; } + // We must advance over the last element. - $this->intCursor += 1; + ++$this->intCursor; // Check the index again, see #461. - return ($this->getCount() == $this->intCursor) ? false : $this; + return ($this->getCount() === $this->intCursor) ? false : $this; } /** @@ -205,6 +217,7 @@ public function prev() } $this->intCursor--; + return $this; } @@ -232,7 +245,7 @@ public function reset() */ public function getClass() { - $arrClass = array(); + $arrClass = []; if ($this->intCursor == 0) { $arrClass[] = 'first'; } @@ -246,7 +259,8 @@ public function getClass() } else { $arrClass[] = 'odd'; } - return implode(' ', $arrClass); + + return \implode(' ', $arrClass); } /** @@ -254,7 +268,10 @@ public function getClass() */ public function parseValue($strOutputFormat = 'text', $objSettings = null) { - return $this->getItem()->parseValue($strOutputFormat, $objSettings); + $item = $this->getItem(); + assert($item instanceof IItem); + + return $item->parseValue($strOutputFormat, $objSettings); } /** @@ -264,7 +281,7 @@ public function parseValue($strOutputFormat = 'text', $objSettings = null) */ public function parseAll($strOutputFormat = 'text', $objSettings = null) { - $arrResult = array(); + $arrResult = []; // Buffer cursor. $intCursor = $this->intCursor; diff --git a/src/MetaModel.php b/src/MetaModel.php index 19e1dbf09..04bdc457a 100644 --- a/src/MetaModel.php +++ b/src/MetaModel.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -21,13 +21,16 @@ * @author David Molineus * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels; +use Contao\Database; +use Contao\System; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\QueryBuilder; use MetaModels\Attribute\IAttribute; @@ -37,6 +40,7 @@ use MetaModels\Filter\Filter; use MetaModels\Filter\IFilter; use MetaModels\Filter\Rules\StaticIdList; +use MetaModels\Helper\LocaleUtil; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -48,6 +52,7 @@ * @SuppressWarnings(PHPMD.ExcessiveClassLength) * @SuppressWarnings(PHPMD.TooManyPublicMethods) * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class MetaModel implements IMetaModel { @@ -58,7 +63,7 @@ class MetaModel implements IMetaModel * * @var array */ - protected $arrData = array(); + protected $arrData = []; /** * This holds all attribute instances. @@ -67,47 +72,49 @@ class MetaModel implements IMetaModel * * @var array */ - protected $arrAttributes = array(); + protected $arrAttributes = []; /** * The service container. * - * @var IMetaModelsServiceContainer + * @var \Closure|IMetaModelsServiceContainer|null + * + * @psalm-suppress DeprecatedInterface */ - protected $serviceContainer; + protected $serviceContainer = null; /** * The database connection. * * @var Connection */ - private $connection; + private Connection $connection; /** * The event dispatcher. * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * The system columns to preserve. * * @var string[] */ - private $systemColumns; + private mixed $systemColumns; /** * The cache existing ids. * * @var array */ - private $existingIds = []; + private array $existingIds = []; /** * Instantiate a MetaModel. * - * @param array $arrData The information array, for information on the available + * @param array $arrData The information array, for information on the available * columns, refer to documentation of table tl_metamodel. * @param EventDispatcherInterface|null $dispatcher The event dispatcher. * @param Connection|null $connection The database connection. @@ -120,11 +127,9 @@ public function __construct( foreach ($arrData as $strKey => $varValue) { $this->arrData[$strKey] = $this->tryUnserialize($varValue); } - $this->systemColumns = array_key_exists('system_columns', $arrData) ? $arrData['system_columns'] : []; + $this->systemColumns = \array_key_exists('system_columns', $arrData) ? $arrData['system_columns'] : []; - $this->connection = $connection; - $this->dispatcher = $dispatcher; - if (null === $this->dispatcher) { + if (null === $dispatcher) { // @codingStandardsIgnoreStart @trigger_error( 'Not passing the event dispatcher as 2nd argument to "' . __METHOD__ . '" is deprecated ' . @@ -132,8 +137,13 @@ public function __construct( E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + + $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); } - if (null === $this->connection) { + $this->dispatcher = $dispatcher; + + if (null === $connection) { // @codingStandardsIgnoreStart @trigger_error( 'Not passing the database connection as 3rd argument to "' . __METHOD__ . '" is deprecated ' . @@ -141,7 +151,11 @@ public function __construct( E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + + $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); } + $this->connection = $connection; } /** @@ -157,7 +171,14 @@ public function getServiceContainer() E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - return is_callable($this->serviceContainer) ? call_user_func($this->serviceContainer) : $this->serviceContainer; + + if (null === $this->serviceContainer) { + throw new \RuntimeException('Deprecated service container has not been set.'); + } + + return \is_callable($this->serviceContainer) + ? \call_user_func($this->serviceContainer) + : $this->serviceContainer; } /** @@ -166,11 +187,14 @@ public function getServiceContainer() * NOTE: this is deprecated - to prevent triggering deprecation notices, you may pass a closure here which * will then return the service container. * - * @param \Closure|IMetaModelsServiceContainer $serviceContainer The service container. + * @param \Closure|IMetaModelsServiceContainer $serviceContainer The service container. + * @param bool $deprecationNotice Send deprecation notice. * * @return MetaModel * * @deprecated Inject services via constructor or setter. + * + * @psalm-suppress DeprecatedInterface */ public function setServiceContainer($serviceContainer, $deprecationNotice = true) { @@ -190,7 +214,7 @@ public function setServiceContainer($serviceContainer, $deprecationNotice = true /** * Retrieve the database instance to use. * - * @return \Contao\Database + * @return Database * * @deprecated Use the doctrine connection instead. */ @@ -202,6 +226,7 @@ protected function getDatabase() E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedMethod */ return $this->getServiceContainer()->getDatabase(); } @@ -212,11 +237,13 @@ protected function getDatabase() * * @return mixed */ - protected function tryUnserialize($value) + protected function tryUnserialize(mixed $value): mixed { - if (!is_array($value) - && 0 === strpos($value, 'a:') - && is_array($unSerialized = unserialize($value, ['allowed_classes' => false]))) { + if ( + \is_string($value) + && \str_starts_with($value, 'a:') + && \is_array($unSerialized = \unserialize($value, ['allowed_classes' => false])) + ) { return $unSerialized; } @@ -253,7 +280,7 @@ public function addAttribute(IAttribute $objAttribute) */ public function hasAttribute($strAttributeName) { - return array_key_exists($strAttributeName, $this->arrAttributes); + return \array_key_exists($strAttributeName, $this->arrAttributes); } /** @@ -301,7 +328,7 @@ protected function isTranslatedAttribute($objAttribute) */ protected function getAttributeImplementing($interface) { - $result = array(); + $result = []; foreach ($this->getAttributes() as $colName => $attribute) { if ($attribute instanceof $interface) { $result[$colName] = $attribute; @@ -346,7 +373,7 @@ protected function getTranslatedAttributes() * * @param IFilter|null $objFilter The filter to search the matching ids for. * - * @return array all matching Ids. + * @return list all matching Ids. */ protected function getMatchingIds($objFilter) { @@ -362,8 +389,8 @@ protected function getMatchingIds($objFilter) return $this->getConnection()->createQueryBuilder() ->select('t.id') ->from($this->getTableName(), 't') - ->execute() - ->fetchAll(\PDO::FETCH_COLUMN); + ->executeQuery() + ->fetchFirstColumn(); } /** @@ -377,7 +404,7 @@ protected function getMatchingIds($objFilter) */ protected function buildDatabaseParameterList($parameters) { - return implode(',', array_fill(0, count($parameters), '?')); + return \implode(',', \array_fill(0, \count($parameters), '?')); } /** @@ -385,7 +412,6 @@ protected function buildDatabaseParameterList($parameters) * * @param string[] $arrIds The ids of the items to retrieve the order of ids is used for sorting of the return * values. - * * @param string[] $arrAttrOnly Names of the attributes that shall be contained in the result, defaults to array() * which means all attributes. * @@ -402,20 +428,20 @@ protected function fetchRows($arrIds, $arrAttrOnly = array()) ->select('t.*') ->from($this->getTableName(), 't') ->where($builder->expr()->in('t.id', ':values')) - ->setParameter('values', $arrIds, Connection::PARAM_STR_ARRAY) + ->setParameter('values', $arrIds, ArrayParameterType::STRING) ->orderBy('FIELD(id, :values)') - ->execute(); + ->executeQuery(); // If we have an attribute restriction, make sure we keep the system columns. See #196. if ($arrAttrOnly) { - $arrAttrOnly = array_merge($this->systemColumns, $arrAttrOnly); + $arrAttrOnly = \array_merge($this->systemColumns, $arrAttrOnly); } $result = []; - while ($row = $query->fetch(\PDO::FETCH_ASSOC)) { + while ($row = $query->fetchAssociative()) { $data = []; foreach ($row as $attribute => $value) { - if ((!$arrAttrOnly) || (in_array($attribute, $arrAttrOnly, true))) { + if ((!$arrAttrOnly) || (\in_array($attribute, $arrAttrOnly, true))) { $data[$attribute] = $value; } } @@ -429,10 +455,9 @@ protected function fetchRows($arrIds, $arrAttrOnly = array()) /** * This method is called to retrieve the data for certain items from the database. * - * @param ITranslated $attribute The attribute to fetch the values for. - * - * @param string[] $ids The ids of the items to retrieve the order of ids is used for sorting of the return - * values. + * @param ITranslated $attribute The attribute to fetch the values for. + * @param list $ids The ids of the items to retrieve the order of ids is used for sorting of the + * return values. * * @return array an array of all matched items, sorted by the id list. * @@ -440,11 +465,13 @@ protected function fetchRows($arrIds, $arrAttrOnly = array()) */ protected function fetchTranslatedAttributeValues(ITranslated $attribute, $ids) { + /** @psalm-suppress DeprecatedMethod */ $attributeData = $attribute->getTranslatedDataFor($ids, $this->getActiveLanguage()); - $missing = array_diff($ids, array_keys($attributeData)); + $missing = \array_values(\array_diff($ids, array_keys($attributeData))); if ($missing) { - $attributeData += $attribute->getTranslatedDataFor($missing, $this->getFallbackLanguage()); + /** @psalm-suppress DeprecatedMethod */ + $attributeData += $attribute->getTranslatedDataFor($missing, $this->getFallbackLanguage() ?? ''); } return $attributeData; @@ -453,22 +480,20 @@ protected function fetchTranslatedAttributeValues(ITranslated $attribute, $ids) /** * This method is called to retrieve the data for certain items from the database. * - * @param string[] $ids The ids of the items to retrieve the order of ids is used for sorting of the - * return values. - * - * @param array $result The current values. - * - * @param string[] $attrOnly Names of the attributes that shall be contained in the result, defaults to array() - * which means all attributes. + * @param list $ids The ids of the items to retrieve the order of ids is used for sorting of the + * return values. + * @param array $result The current values. + * @param string[] $attrOnly Names of the attributes that shall be contained in the result, defaults to array() + * which means all attributes. * * @return array an array of all matched items, sorted by the id list. */ - protected function fetchAdditionalAttributes($ids, $result, $attrOnly = array()) + protected function fetchAdditionalAttributes($ids, $result, $attrOnly = []) { $attributes = $this->getAttributeByNames($attrOnly); - $attributeNames = array_intersect( - array_keys($attributes), - array_keys(array_merge($this->getComplexAttributes(), $this->getTranslatedAttributes())) + $attributeNames = \array_intersect( + \array_keys($attributes), + \array_keys(\array_merge($this->getComplexAttributes(), $this->getTranslatedAttributes())) ); foreach ($attributeNames as $attributeName) { @@ -479,14 +504,17 @@ protected function fetchAdditionalAttributes($ids, $result, $attrOnly = array()) // If it is translated, fetch the translated data now. if ($this->isTranslatedAttribute($attribute)) { - /** @var ITranslated $attribute */ + /** + * @var ITranslated $attribute + * @psalm-suppress DeprecatedMethod + */ $attributeData = $this->fetchTranslatedAttributeValues($attribute, $ids); } else { /** @var IComplex $attribute */ $attributeData = $attribute->getDataFor($ids); } - foreach (array_keys($result) as $id) { + foreach (\array_keys($result) as $id) { $result[$id][$attributeName] = ($attributeData[$id] ?? null); } } @@ -497,24 +525,23 @@ protected function fetchAdditionalAttributes($ids, $result, $attrOnly = array()) /** * This method is called to retrieve the data for certain items from the database. * - * @param int[] $arrIds The ids of the items to retrieve the order of ids is used for sorting of the - * return values. - * - * @param string[] $arrAttrOnly Names of the attributes that shall be contained in the result, defaults to array() - * which means all attributes. + * @param list $arrIds The ids of the items to retrieve the order of ids is used for sorting of the + * return values. + * @param string[] $arrAttrOnly Names of the attributes that shall be contained in the result, defaults to + * array() which means all attributes. * - * @return \MetaModels\IItems a collection of all matched items, sorted by the id list. + * @return IItems a collection of all matched items, sorted by the id list. */ - protected function getItemsWithId($arrIds, $arrAttrOnly = array()) + protected function getItemsWithId($arrIds, $arrAttrOnly = []) { - $arrIds = array_unique(array_filter($arrIds)); + $arrIds = \array_values(\array_unique(\array_filter($arrIds))); if (!$arrIds) { - return new Items(array()); + return new Items([]); } if (!$arrAttrOnly) { - $arrAttrOnly = array_keys($this->getAttributes()); + $arrAttrOnly = \array_keys($this->getAttributes()); } $arrResult = $this->fetchRows($arrIds, $arrAttrOnly); @@ -525,9 +552,9 @@ protected function getItemsWithId($arrIds, $arrAttrOnly = array()) $strColName = $objAttribute->getColName(); // Run each row. - foreach (array_keys($arrResult) as $intId) { + foreach (\array_keys($arrResult) as $intId) { // Do only skip if the key does not exist. Do not use isset() here as "null" is a valid value. - if (!array_key_exists($strColName, $arrResult[$intId])) { + if (!\array_key_exists($strColName, $arrResult[$intId])) { continue; } $value = $arrResult[$intId][$strColName]; @@ -536,8 +563,8 @@ protected function getItemsWithId($arrIds, $arrAttrOnly = array()) if ($value === $value2) { $value2 = $this->tryUnserialize($value); if ($value !== $value2) { - trigger_error( - sprintf( + \trigger_error( + \sprintf( 'Attribute type %s should implement method unserializeData() and serializeData().', $objAttribute->get('type') ), @@ -552,7 +579,7 @@ protected function getItemsWithId($arrIds, $arrAttrOnly = array()) // Determine "independent attributes" (complex and translated) and inject their content into the row. $arrResult = $this->fetchAdditionalAttributes($arrIds, $arrResult, $arrAttrOnly); - $arrItems = array(); + $arrItems = []; foreach ($arrResult as $arrEntry) { $arrItems[] = new Item($this, $arrEntry, $this->dispatcher); } @@ -574,6 +601,7 @@ protected function copyFilter($objFilter) } else { $objNewFilter = $this->getEmptyFilter(); } + return $objNewFilter; } @@ -584,12 +612,12 @@ public function get($strKey) { // Try to retrieve via getter method. $strGetter = 'get' . $strKey; - if (method_exists($this, $strGetter)) { + if (\method_exists($this, $strGetter)) { return $this->$strGetter(); } // Return via raw array if available. - if (array_key_exists($strKey, $this->arrData)) { + if (\array_key_exists($strKey, $this->arrData)) { return $this->arrData[$strKey]; } @@ -601,7 +629,7 @@ public function get($strKey) */ public function getTableName() { - return ($this->arrData['tableName'] ?? null); + return ($this->arrData['tableName'] ?? ''); } /** @@ -629,12 +657,14 @@ public function getInVariantAttributes() if (!$this->hasVariants()) { return $arrAttributes; } + // Remove all attributes that are selected for overriding. foreach ($arrAttributes as $strAttributeId => $objAttribute) { if ($objAttribute->get('isvariant')) { unset($arrAttributes[$strAttributeId]); } } + return $arrAttributes; } @@ -648,20 +678,22 @@ public function isTranslated(bool $deprecation = true) if (!$deprecation) { return $this->arrData['translated']; } + if ($this instanceof ITranslatedMetaModel) { // @codingStandardsIgnoreStart @\trigger_error( - sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please use "instanceof \MetaModels\ITranslatedMetaModel" instead.', __METHOD__), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd return true; } + if ($this->arrData['translated']) { // @codingStandardsIgnoreStart @\trigger_error( - sprintf( + \sprintf( 'The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'MetaModel "%s" should implement "\MetaModels\ITranslatedMetaModel" instead.', __METHOD__, @@ -670,12 +702,13 @@ public function isTranslated(bool $deprecation = true) E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + return true; } // @codingStandardsIgnoreStart @\trigger_error( - sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please use "instanceof \MetaModels\ITranslatedMetaModel" instead.', __METHOD__), E_USER_DEPRECATED ); @@ -702,7 +735,7 @@ public function getAvailableLanguages() if ($this instanceof ITranslatedMetaModel) { // @codingStandardsIgnoreStart @\trigger_error( - sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please use "\MetaModels\ITranslatedMetaModel::getLanguages" instead.', __METHOD__), E_USER_DEPRECATED ); @@ -710,21 +743,22 @@ public function getAvailableLanguages() return $this->getLanguages(); } + /** @psalm-suppress DeprecatedMethod */ if ($this->isTranslated(false)) { // @codingStandardsIgnoreStart @\trigger_error( - sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please test for "instanceof "\MetaModels\ITranslatedMetaModel" and use '. '"\MetaModels\ITranslatedMetaModel::getLanguages" instead.', __METHOD__), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - return array_keys((array) $this->arrData['languages']); + return \array_keys((array) $this->arrData['languages']); } // @codingStandardsIgnoreStart @\trigger_error( - sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please test for "instanceof \MetaModels\ITranslatedMetaModel".', __METHOD__), E_USER_DEPRECATED ); @@ -743,7 +777,7 @@ public function getFallbackLanguage() if ($this instanceof ITranslatedMetaModel) { // @codingStandardsIgnoreStart @\trigger_error( - sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please use "\MetaModels\ITranslatedMetaModel::getMainLanguage" instead.', __METHOD__), E_USER_DEPRECATED ); @@ -751,10 +785,11 @@ public function getFallbackLanguage() return $this->getMainLanguage(); } + /** @psalm-suppress DeprecatedMethod */ if ($this->isTranslated(false)) { // @codingStandardsIgnoreStart @\trigger_error( - sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please implement interface "\MetaModels\ITranslatedMetaModel" and use ' . '"\MetaModels\ITranslatedMetaModel::getMainLanguage" instead.', __METHOD__), E_USER_DEPRECATED @@ -768,7 +803,7 @@ public function getFallbackLanguage() } // @codingStandardsIgnoreStart @\trigger_error( - sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please test for translations via "instanceof \MetaModels\ITranslatedMetaModel" and call ' . '"\MetaModels\ITranslatedMetaModel::getMainLanguage" instead.', __METHOD__), E_USER_DEPRECATED @@ -793,22 +828,24 @@ public function getActiveLanguage() if ($this instanceof ITranslatedMetaModel) { // @codingStandardsIgnoreStart @\trigger_error( - sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please use "\MetaModels\ITranslatedMetaModel::getLanguage" and ' . '"\MetaModels\ITranslatedMetaModel::selectLanguage" instead.', __METHOD__), E_USER_DEPRECATED ); + // @codingStandardsIgnoreEnd } else { // @codingStandardsIgnoreStart @\trigger_error( - sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('The method "%s" is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please use "\MetaModels\ITranslatedMetaModel::getLanguage" instead.', __METHOD__), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd } - return \str_replace('-', '_', $GLOBALS['TL_LANGUAGE']); + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + return LocaleUtil::formatAsLocale($GLOBALS['TL_LANGUAGE'] ?? 'en'); } /** @@ -817,6 +854,7 @@ public function getActiveLanguage() public function getAttribute($strAttributeName) { $arrAttributes = $this->getAttributes(); + return ($arrAttributes[$strAttributeName] ?? null); } @@ -826,10 +864,11 @@ public function getAttribute($strAttributeName) public function getAttributeById($intId) { foreach ($this->getAttributes() as $objAttribute) { - if ($objAttribute->get('id') === $intId) { + if ((int) $objAttribute->get('id') === $intId) { return $objAttribute; } } + return null; } @@ -860,15 +899,17 @@ protected function getAttributeByNames($attrNames = []) /** * {@inheritdoc} */ - public function findById($intId, $arrAttrOnly = array()) + public function findById($intId, $arrAttrOnly = []) { if (!$intId) { return null; } - $objItems = $this->getItemsWithId(array($intId), $arrAttrOnly); - if ($objItems && $objItems->first()) { + + $objItems = $this->getItemsWithId([$intId], $arrAttrOnly); + if ($objItems->first()) { return $objItems->getItem(); } + return null; } @@ -881,7 +922,7 @@ public function findByFilter( $intOffset = 0, $intLimit = 0, $strSortOrder = 'ASC', - $arrAttrOnly = array() + $arrAttrOnly = [] ) { return $this->getItemsWithId( $this->getIdsFromFilter( @@ -902,7 +943,7 @@ public function findByFilter( */ public function getIdsFromFilter($objFilter, $strSortBy = '', $intOffset = 0, $intLimit = 0, $strSortOrder = 'ASC') { - if ([] === $arrFilteredIds = array_filter($this->getMatchingIds($objFilter))) { + if ([] === $arrFilteredIds = \array_values(\array_filter($this->getMatchingIds($objFilter)))) { return []; } @@ -912,28 +953,28 @@ public function getIdsFromFilter($objFilter, $strSortBy = '', $intOffset = 0, $i $arrFilteredIds = $objSortAttribute->sortIds($arrFilteredIds, $strSortOrder); } elseif ('id' === $strSortBy) { if ($strSortOrder === 'ASC') { - asort($arrFilteredIds); + \asort($arrFilteredIds); } else { - rsort($arrFilteredIds); + \rsort($arrFilteredIds); } - } elseif (in_array($strSortBy, array('pid', 'tstamp', 'sorting'))) { + } elseif (\in_array($strSortBy, array('pid', 'tstamp', 'sorting'))) { // Build the right key for the cache. $sortKey = \sprintf('%s-%s', $strSortBy, \strtolower($strSortOrder)); // Used the cached ID list, and make a list of wanted ID's with the sorting of the cache. if (!isset($this->existingIds[$sortKey])) { $this->existingIds[$sortKey] = []; } - $cacheResult = array_intersect($this->existingIds[$sortKey], $arrFilteredIds); + $cacheResult = \array_intersect($this->existingIds[$sortKey], $arrFilteredIds); // Check if we have all ID's or if we have one missing, now we are using the order of the MM Filter. - if (array_intersect($arrFilteredIds, $cacheResult) === $arrFilteredIds) { + if (\array_intersect($arrFilteredIds, $cacheResult) === $arrFilteredIds) { if ($intOffset > 0 || $intLimit > 0) { - return array_values(array_slice($cacheResult, $intOffset, $intLimit ?: null)); + return \array_values(\array_slice($cacheResult, $intOffset, $intLimit ?: null)); } - return array_values($cacheResult); + return \array_values($cacheResult); } // Merge the already known and the new one. - $fullIdList = array_merge((array) $this->existingIds[$sortKey], $arrFilteredIds); + $fullIdList = \array_merge((array) $this->existingIds[$sortKey], $arrFilteredIds); $fullIdList = \array_keys(\array_flip($fullIdList)); // Sort by database values. @@ -943,24 +984,24 @@ public function getIdsFromFilter($objFilter, $strSortBy = '', $intOffset = 0, $i ->select('t.id') ->from($this->getTableName(), 't') ->where($builder->expr()->in('t.id', ':values')) - ->setParameter('values', $arrFilteredIds, Connection::PARAM_STR_ARRAY) + ->setParameter('values', $arrFilteredIds, ArrayParameterType::STRING) ->orderBy($strSortBy, $strSortOrder) - ->execute() - ->fetchAll(\PDO::FETCH_COLUMN); + ->executeQuery() + ->fetchFirstColumn(); // Add the new sorted Id's to the cache and use only the wanted. $this->existingIds[$sortKey] = $arrSortedFilteredIds; - $arrFilteredIds = array_intersect($arrSortedFilteredIds, $arrFilteredIds); - } elseif ($strSortBy == 'random') { - shuffle($arrFilteredIds); + $arrFilteredIds = \array_intersect($arrSortedFilteredIds, $arrFilteredIds); + } elseif ($strSortBy === 'random') { + \shuffle($arrFilteredIds); } } // Apply limiting then. if ($intOffset > 0 || $intLimit > 0) { - $arrFilteredIds = array_slice($arrFilteredIds, $intOffset, $intLimit ?: null); + $arrFilteredIds = \array_slice($arrFilteredIds, $intOffset, $intLimit ?: null); } - return array_values($arrFilteredIds); + return \array_values($arrFilteredIds); } /** @@ -969,7 +1010,7 @@ public function getIdsFromFilter($objFilter, $strSortBy = '', $intOffset = 0, $i public function getCount($objFilter) { $arrFilteredIds = $this->getMatchingIds($objFilter); - if (0 === count($arrFilteredIds)) { + if (0 === \count($arrFilteredIds)) { return 0; } @@ -979,9 +1020,9 @@ public function getCount($objFilter) ->select('COUNT(t.id)') ->from($this->getTableName(), 't') ->where($builder->expr()->in('t.id', ':values')) - ->setParameter('values', $arrFilteredIds, Connection::PARAM_STR_ARRAY) - ->execute() - ->fetch(\PDO::FETCH_COLUMN); + ->setParameter('values', $arrFilteredIds, ArrayParameterType::STRING) + ->executeQuery() + ->fetchOne(); } /** @@ -997,10 +1038,11 @@ public function findVariantBase($objFilter) ->select('t.id') ->from($this->getTableName(), 't') ->where('t.varbase=1') - ->execute() - ->fetchAll(\PDO::FETCH_COLUMN); + ->executeQuery() + ->fetchFirstColumn(); $objNewFilter->addFilterRule(new StaticIdList($idList)); + return $this->findByFilter($objNewFilter); } @@ -1011,7 +1053,7 @@ public function findVariants($arrIds, $objFilter) { if (!$arrIds) { // Return an empty result. - return $this->getItemsWithId(array()); + return $this->getItemsWithId([]); } $objNewFilter = $this->copyFilter($objFilter); @@ -1022,11 +1064,12 @@ public function findVariants($arrIds, $objFilter) ->from($this->getTableName(), 't') ->where('t.varbase=0') ->andWhere($builder->expr()->in('t.vargroup', ':ids')) - ->setParameter('ids', $arrIds, Connection::PARAM_STR_ARRAY) - ->execute() - ->fetchAll(\PDO::FETCH_COLUMN); + ->setParameter('ids', $arrIds, ArrayParameterType::STRING) + ->executeQuery() + ->fetchFirstColumn(); $objNewFilter->addFilterRule(new StaticIdList($idList)); + return $this->findByFilter($objNewFilter); } @@ -1048,11 +1091,12 @@ public function findVariantsWithBase($arrIds, $objFilter) ->from($this->getTableName(), 't') ->leftJoin('t', $this->getTableName(), 't2', 't.vargroup=t2.vargroup') ->where($builder->expr()->in('t2.id', ':ids')) - ->setParameter('ids', $arrIds, Connection::PARAM_STR_ARRAY) - ->execute() - ->fetchAll(\PDO::FETCH_COLUMN); + ->setParameter('ids', $arrIds, ArrayParameterType::STRING) + ->executeQuery() + ->fetchFirstColumn(); $objNewFilter->addFilterRule(new StaticIdList($idList)); + return $this->findByFilter($objNewFilter); } @@ -1066,10 +1110,11 @@ public function getAttributeOptions($strAttribute, $objFilter = null) if ($objFilter) { $arrFilteredIds = $this->getMatchingIds($objFilter); $arrFilteredIds = $objAttribute->sortIds($arrFilteredIds, 'ASC'); + return $objAttribute->getFilterOptions($arrFilteredIds, true); - } else { - return $objAttribute->getFilterOptions(null, true); } + + return $objAttribute->getFilterOptions(null, true); } return []; @@ -1079,38 +1124,33 @@ public function getAttributeOptions($strAttribute, $objFilter = null) * Update the value of a native column for the given ids with the given data. * * @param string $strColumn The column name to update (i.e. tstamp). - * * @param array $arrIds The ids of the rows that shall be updated. - * * @param mixed $varData The data to save. If this is an array, it is automatically serialized. * * @return void */ protected function saveSimpleColumn($strColumn, $arrIds, $varData) { - if (is_array($varData)) { - $varData = serialize($varData); + if (\is_array($varData)) { + $varData = \serialize($varData); } $builder = $this->getConnection()->createQueryBuilder(); $builder ->update($this->getTableName(), 't2') - ->set('t2.' . $strColumn, is_array($varData) ? serialize($varData) : $varData) + ->set('t2.' . $strColumn, \is_array($varData) ? \serialize($varData) : $varData) ->where($builder->expr()->in('t2.id', ':ids')) - ->setParameter('ids', $arrIds, Connection::PARAM_STR_ARRAY) - ->execute(); + ->setParameter('ids', $arrIds, ArrayParameterType::STRING) + ->executeQuery(); } /** * Update an attribute for the given ids with the given data. * * @param IAttribute $objAttribute The attribute to save. - * * @param array $arrIds The ids of the rows that shall be updated. - * * @param mixed $varData The data to save in raw data. - * * @param string $strLangCode The language code to save. * * @return void @@ -1125,7 +1165,7 @@ protected function saveAttribute($objAttribute, $arrIds, $varData, $strLangCode) $varData = $objAttribute->serializeData($varData); } - $arrData = array(); + $arrData = []; foreach ($arrIds as $intId) { $arrData[$intId] = $varData; } @@ -1142,7 +1182,7 @@ protected function saveAttribute($objAttribute, $arrIds, $varData, $strLangCode) } else { throw new \RuntimeException( 'Unknown attribute type, can not save. Interfaces implemented: ' . - implode(', ', class_implements($objAttribute)) + \implode(', ', \class_implements($objAttribute)) ); } } @@ -1151,11 +1191,8 @@ protected function saveAttribute($objAttribute, $arrIds, $varData, $strLangCode) * Update the variants with the value if needed. * * @param IItem $item The item to save. - * * @param string $activeLanguage The language the values are in. - * * @param int[] $allIds The ids of all variants. - * * @param bool $baseAttributes If also the base attributes get updated as well. * * @return void @@ -1179,6 +1216,7 @@ protected function updateVariants($item, $activeLanguage, $allIds, $baseAttribut } else { $arrIds = array($item->get('id')); } + $this->saveAttribute($objAttribute, $arrIds, $item->get($strAttributeId), $activeLanguage); } } @@ -1216,7 +1254,7 @@ protected function createNewItem($item) ->insert($this->getTableName()) ->values($parameters) ->setParameters($data) - ->execute(); + ->executeQuery(); $item->set('id', $connection->lastInsertId()); @@ -1238,28 +1276,32 @@ public function saveItem($objItem, $timestamp = null) E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + + $timestamp = \time(); } $baseAttributes = $this->saveBaseColumns($objItem, $timestamp ?: \time()); + /** @psalm-suppress DeprecatedMethod */ if ($this instanceof ITranslatedMetaModel) { $strActiveLanguage = $this->getLanguage(); } elseif ($this->isTranslated(false)) { // @codingStandardsIgnoreStart @\trigger_error( - sprintf('Support for translated MetaModels not implementing "\MetaModels\ITranslatedMetaModel" '. - 'is deprecated since MetaModels 2.2 and to be removed in 3.0. ' . + \sprintf('Support for translated MetaModels not implementing "\MetaModels\ITranslatedMetaModel" '. + 'is %s deprecated since MetaModels 2.2 and to be removed in 3.0. ' . 'Please implement interface "\MetaModels\ITranslatedMetaModel".', __METHOD__), E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + /** @psalm-suppress DeprecatedMethod */ $strActiveLanguage = $this->getActiveLanguage(); } else { - $strActiveLanguage = null; + $strActiveLanguage = ''; } $arrAllIds = array(); if ($objItem->isVariantBase()) { - $objVariants = $this->findVariantsWithBase(array($objItem->get('id')), null); + $objVariants = $this->findVariantsWithBase([$objItem->get('id')], null); foreach ($objVariants as $objVariant) { /** @var IItem $objVariant */ $arrAllIds[] = $objVariant->get('id'); @@ -1281,10 +1323,11 @@ public function saveItem($objItem, $timestamp = null) */ public function delete(IItem $objItem) { - $arrIds = array($objItem->get('id')); + $arrIds = [$objItem->get('id')]; // Determine if the model is a variant base and if so, fetch the variants additionally. if ($objItem->isVariantBase()) { $objVariants = $objItem->getVariants(new Filter($this)); + assert($objVariants instanceof IItems); foreach ($objVariants as $objVariant) { /** @var IItem $objVariant */ $arrIds[] = $objVariant->get('id'); @@ -1304,8 +1347,8 @@ public function delete(IItem $objItem) $builder ->delete($this->getTableName()) ->where($builder->expr()->in($this->getTableName() . '.id', ':ids')) - ->setParameter('ids', $arrIds, Connection::PARAM_STR_ARRAY) - ->execute(); + ->setParameter('ids', $arrIds, ArrayParameterType::STRING) + ->executeQuery(); } /** @@ -1331,6 +1374,7 @@ public function prepareFilter($intFilterSettings, $arrFilterUrl) $objFilter = $this->getEmptyFilter(); if ($intFilterSettings) { + /** @psalm-suppress DeprecatedMethod */ $objFilterSettings = $this->getServiceContainer()->getFilterFactory()->createCollection($intFilterSettings); $objFilterSettings->addRules($objFilter, $arrFilterUrl); } @@ -1350,26 +1394,18 @@ public function getView($intViewId = 0) ); // @codingStandardsIgnoreEnd - return $this->getServiceContainer()->getRenderSettingFactory()->createCollection($this, $intViewId); + /** @psalm-suppress DeprecatedMethod */ + return $this->getServiceContainer()->getRenderSettingFactory()->createCollection($this, (string) $intViewId); } /** * Obtain the doctrine connection. * * @return Connection - * - * @throws \ReflectionException Throws could not connect to database. */ private function getConnection() { - if ($this->connection) { - return $this->connection; - } - - $reflection = new \ReflectionProperty(\Contao\Database::class, 'resConnection'); - $reflection->setAccessible(true); - - return $this->connection = $reflection->getValue($this->getDatabase()); + return $this->connection; } /** @@ -1380,7 +1416,7 @@ private function getConnection() * * @return bool */ - private function saveBaseColumns(IItem $item, $timestamp) + private function saveBaseColumns(IItem $item, int $timestamp): bool { $isNew = false; $item->set('tstamp', $timestamp); diff --git a/src/MetaModelsEvents.php b/src/MetaModelsEvents.php index a620c7650..c3d19bdbd 100644 --- a/src/MetaModelsEvents.php +++ b/src/MetaModelsEvents.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. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @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 */ @@ -34,7 +35,7 @@ class MetaModelsEvents * * @see MetaModelsEvents::SUBSYSTEM_BOOT_BACKEND */ - const SUBSYSTEM_BOOT = 'metamodels.boot'; + public const SUBSYSTEM_BOOT = 'metamodels.boot'; /** * Event for booting a MetaModels subsystem in the frontend. @@ -43,7 +44,7 @@ class MetaModelsEvents * * @see \MetaModels\Events\MetaModelsBootEvent */ - const SUBSYSTEM_BOOT_FRONTEND = 'metamodels.boot.frontend'; + public const SUBSYSTEM_BOOT_FRONTEND = 'metamodels.boot.frontend'; /** * Event for booting a MetaModels subsystem in the backend. @@ -52,40 +53,40 @@ class MetaModelsEvents * * @see \MetaModels\Events\MetaModelsBootEvent */ - const SUBSYSTEM_BOOT_BACKEND = 'metamodels.boot.backend'; + public const SUBSYSTEM_BOOT_BACKEND = 'metamodels.boot.backend'; /** * Event when a attribute factory is created. * * @see \MetaModels\Attribute\Events\CreateAttributeFactoryEvent */ - const ATTRIBUTE_FACTORY_CREATE = 'metamodels.attribute.factory.create'; + public const ATTRIBUTE_FACTORY_CREATE = 'metamodels.attribute.factory.create'; /** * Event when a filter setting factory is created. * * @see \MetaModels\Filter\Setting\Events\CreateFilterSettingFactoryEvent */ - const FILTER_SETTING_FACTORY_CREATE = 'metamodels.filter-setting.factory.create'; + public const FILTER_SETTING_FACTORY_CREATE = 'metamodels.filter-setting.factory.create'; /** - * Event when a filter setting factory is created. + * Event when a render setting factory is created. * - * @see \MetaModels\Filter\Setting\Events\CreateRenderSettingFactoryEvent + * @see \MetaModels\Render\Setting\Events\CreateRenderSettingFactoryEvent */ - const RENDER_SETTING_FACTORY_CREATE = 'metamodels.render-setting.factory.create'; + public const RENDER_SETTING_FACTORY_CREATE = 'metamodels.render-setting.factory.create'; /** * Event when an item is parsed. * * @see \MetaModels\Events\ParseItemEvent. */ - const PARSE_ITEM = 'metamodels.parse-item'; + public const PARSE_ITEM = 'metamodels.parse-item'; /** * Event when an item list is rendered. * * @see \MetaModels\Events\RenderItemListEvent. */ - const RENDER_ITEM_LIST = 'metamodels.render-item-list'; + public const RENDER_ITEM_LIST = 'metamodels.render-item-list'; } diff --git a/src/MetaModelsServiceContainer.php b/src/MetaModelsServiceContainer.php index 916e5e108..1ead59852 100644 --- a/src/MetaModelsServiceContainer.php +++ b/src/MetaModelsServiceContainer.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,17 +15,25 @@ * @author Sven Baumann * @author David Molineus * @author Richard Henkenjohann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels; +use Closure; use Contao\BackendUser; +use Contao\Database; use Contao\FrontendUser; +use Contao\System; +use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; use Doctrine\Common\Cache\Cache; +use Doctrine\DBAL\Connection; +use InvalidArgumentException; use MetaModels\Attribute\IAttributeFactory; +use MetaModels\BackendIntegration\ViewCombinations; use MetaModels\Filter\Setting\IFilterSettingFactory; use MetaModels\Render\Setting\IRenderSettingFactory; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -34,62 +42,67 @@ * Reference implementation of IMetaModelsServiceContainer. * * @deprecated The service container will get removed, use the symfony service container instead. + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + * + * @psalm-suppress DeprecatedInterface + * @psalm-suppress MissingConstructor */ class MetaModelsServiceContainer implements IMetaModelsServiceContainer { /** * The factory to use. * - * @var IFactory + * @var IFactory|callable */ protected $factory; /** * The factory to use. * - * @var IAttributeFactory + * @var IAttributeFactory|callable */ protected $attributeFactory; /** * The filter setting factory. * - * @var IFilterSettingFactory + * @var IFilterSettingFactory|callable */ protected $filterFactory; /** * The render setting factory. * - * @var IRenderSettingFactory + * @var IRenderSettingFactory|callable */ protected $renderFactory; /** * The event dispatcher. * - * @var EventDispatcherInterface + * @var EventDispatcherInterface|callable */ protected $dispatcher; /** * The Contao database instance to use. * - * @var \Contao\Database + * @var Database|callable */ protected $database; /** * The cache in use. * - * @var Cache + * @var Cache|callable */ protected $cache; /** * Registered services. * - * @var object[] + * @var array */ protected $services; @@ -123,6 +136,7 @@ public function getFactory() if (\is_callable($this->factory)) { $this->factory = \call_user_func($this->factory); + /** @psalm-suppress DeprecatedMethod */ $this->factory->setServiceContainer($this, false); } @@ -159,6 +173,7 @@ public function getAttributeFactory() if (\is_callable($this->attributeFactory)) { $this->attributeFactory = \call_user_func($this->attributeFactory); + /** @psalm-suppress DeprecatedMethod */ $this->attributeFactory->setServiceContainer($this, false); } @@ -195,6 +210,7 @@ public function getFilterFactory() if (\is_callable($this->filterFactory)) { $this->filterFactory = \call_user_func($this->filterFactory); + /** @psalm-suppress DeprecatedMethod */ $this->filterFactory->setServiceContainer($this, false); } @@ -231,6 +247,7 @@ public function getRenderSettingFactory() if (\is_callable($this->renderFactory)) { $this->renderFactory = \call_user_func($this->renderFactory); + /** @psalm-suppress DeprecatedMethod */ $this->renderFactory->setServiceContainer($this, false); } @@ -275,7 +292,7 @@ public function getEventDispatcher() /** * Set the Contao database instance. * - * @param \Contao\Database|callable $database The contao database instance. + * @param Database|callable $database The contao database instance. * * @return MetaModelsServiceContainer */ @@ -347,7 +364,7 @@ public function setCache($cache) /** * {@inheritdoc} * - * @throws \InvalidArgumentException When the passed service is not an object and no service name has been passed. + * @throws InvalidArgumentException When the passed service is not an object and no service name has been passed. * * @deprecated The service container will get removed, use the symfony service container instead. */ @@ -360,8 +377,8 @@ public function setService($service, $serviceName = null) ); // @codingStandardsIgnoreEnd if ($serviceName === null) { - if (!\is_object($service) || $service instanceof \Closure) { - throw new \InvalidArgumentException( + if (!\is_object($service) || $service instanceof Closure) { + throw new InvalidArgumentException( 'Service name must be given to ' . __CLASS__ . '::setService when not passing a class instance.' ); } @@ -392,29 +409,34 @@ public function getService($serviceName) // @codingStandardsIgnoreEnd // Hacked in here as initialization is dead now. - if (!isset($this->services[(string) $serviceName]) && 'metamodels-view-combinations' === $serviceName) { - $determinator = \System::getContainer()->get('cca.dc-general.scope-matcher'); + if (!isset($this->services[$serviceName]) && 'metamodels-view-combinations' === $serviceName) { + $determinator = System::getContainer()->get('cca.dc-general.scope-matcher'); + assert($determinator instanceof RequestScopeDeterminator); + $connection = System::getContainer()->get('database_connection'); + assert($connection instanceof Connection); switch (true) { case $determinator->currentScopeIsFrontend(): + /** @psalm-suppress DeprecatedClass */ $this->services['metamodels-view-combinations'] = - new \MetaModels\FrontendIntegration\ViewCombinations( + new FrontendIntegration\ViewCombinations( $this, FrontendUser::getInstance(), - \System::getContainer()->get('database_connection') + $connection ); break; case $determinator->currentScopeIsBackend(): + /** @psalm-suppress DeprecatedClass */ $this->services['metamodels-view-combinations'] = - new \MetaModels\BackendIntegration\ViewCombinations( + new ViewCombinations( $this, BackendUser::getInstance(), - \System::getContainer()->get('database_connection') + $connection ); break; default: } } - return ($this->services[(string) $serviceName] ?? null); + return ($this->services[$serviceName] ?? null); } } diff --git a/src/Render/Setting/Collection.php b/src/Render/Setting/Collection.php index 2deca1de8..13fe9e2cd 100644 --- a/src/Render/Setting/Collection.php +++ b/src/Render/Setting/Collection.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,7 +17,7 @@ * @author Ingolf Steinhardt * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,6 +35,7 @@ use MetaModels\IMetaModel; use MetaModels\ITranslatedMetaModel; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Contracts\Translation\TranslatorInterface; /** * Base implementation for render settings. @@ -53,42 +54,42 @@ class Collection implements ICollection * * @var EventDispatcherInterface */ - private $dispatcher; + private EventDispatcherInterface $dispatcher; /** * The filter setting factory. * * @var IFilterSettingFactory */ - private $filterFactory; + private IFilterSettingFactory $filterFactory; /** * The filter URL builder. * * @var FilterUrlBuilder */ - private $filterUrlBuilder; + private FilterUrlBuilder $filterUrlBuilder; /** * The base information for this render settings object. * * @var array */ - protected $arrBase = array(); + protected $arrBase = []; /** * The sub settings for all attributes. * * @var array */ - protected $arrSettings = array(); + protected $arrSettings = []; /** * The jump to information buffered in this setting. * * @var array */ - protected $jumpToCache; + protected $jumpToCache = []; /** * Create a new instance. @@ -97,11 +98,11 @@ class Collection implements ICollection * @param array $arrInformation The array that holds all base information for the new instance. * @param EventDispatcherInterface $dispatcher The event dispatcher. * @param IFilterSettingFactory $filterFactory The filter setting factory. - * @param FilterUrlBuilder $filterUrlBuilder The filter URL builder. + * @param FilterUrlBuilder|null $filterUrlBuilder The filter URL builder. */ public function __construct( IMetaModel $metaModel, - $arrInformation, + array $arrInformation, EventDispatcherInterface $dispatcher, IFilterSettingFactory $filterFactory, FilterUrlBuilder $filterUrlBuilder = null @@ -109,26 +110,7 @@ public function __construct( $this->metaModel = $metaModel; $this->dispatcher = $dispatcher; $this->filterFactory = $filterFactory; - if (null === $this->dispatcher) { - // @codingStandardsIgnoreStart - @trigger_error( - 'Not passing the event dispatcher as 3rd argument to "' . __METHOD__ . '" is deprecated ' . - 'and will cause an error in MetaModels 3.0', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - $this->dispatcher = System::getContainer()->get('event_dispatcher'); - } - if (null === $this->filterFactory) { - // @codingStandardsIgnoreStart - @trigger_error( - 'Not passing the filter setting factory as 4th argument to "' . __METHOD__ . '" is deprecated ' . - 'and will cause an error in MetaModels 3.0', - E_USER_DEPRECATED - ); - // @codingStandardsIgnoreEnd - $this->filterFactory = System::getContainer()->get('metamodels.filter_setting_factory'); - } + if (null === $filterUrlBuilder) { // @codingStandardsIgnoreStart @trigger_error( @@ -138,6 +120,7 @@ public function __construct( ); // @codingStandardsIgnoreEnd $filterUrlBuilder = System::getContainer()->get('metamodels.filter_url'); + assert($filterUrlBuilder instanceof FilterUrlBuilder); } $this->filterUrlBuilder = $filterUrlBuilder; @@ -151,7 +134,7 @@ public function __construct( */ public function get($strName) { - return $this->arrBase[$strName]; + return $this->arrBase[$strName] ?? null; } /** @@ -191,7 +174,7 @@ public function setSetting($strAttributeName, $objSetting) */ public function getSettingNames() { - return array_keys($this->arrSettings); + return \array_keys($this->arrSettings); } /** @@ -201,14 +184,23 @@ public function getSettingNames() * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) + * @psalm-suppress PossiblyNullArrayOffset */ private function getJumpToLabel() { $tableName = $this->metaModel->getTableName(); + if ( + null !== ($label = ($GLOBALS['TL_LANG']['MSC'][$tableName][$this->get('id')]['details'] ?? + ($GLOBALS['TL_LANG']['MSC'][$tableName]['details'] ?? + ($GLOBALS['TL_LANG']['MSC']['details'] ?? null)))) + ) { + return $label; + } + + $translator = System::getContainer()->get('translator'); + assert($translator instanceof TranslatorInterface); - return ($GLOBALS['TL_LANG']['MSC'][$tableName][$this->get('id')]['details'] ?? - ($GLOBALS['TL_LANG']['MSC'][$tableName]['details'] ?? - $GLOBALS['TL_LANG']['MSC']['details'])); + return $translator->trans('details', [], $tableName); } /** @@ -218,16 +210,16 @@ private function getJumpToLabel() * * @return array */ - private function getPageDetails($pageId): array + private function getPageDetails(string $pageId): array { if (empty($pageId)) { return []; } - $event = new GetPageDetailsEvent($pageId); + $event = new GetPageDetailsEvent((int) $pageId); $this->dispatcher->dispatch($event, ContaoEvents::CONTROLLER_GET_PAGE_DETAILS); - return ($event->getPageDetails() ?? []); + return $event->getPageDetails(); } /** @@ -241,11 +233,14 @@ private function determineJumpToInformation(): array $translated = false; $desiredLanguage = null; $fallbackLanguage = null; + + /** @psalm-suppress DeprecatedMethod */ if ($this->metaModel instanceof ITranslatedMetaModel) { $translated = true; $desiredLanguage = $this->metaModel->getLanguage(); $fallbackLanguage = $this->metaModel->getMainLanguage(); - } elseif ($this->metaModel->isTranslated(false)) { + /** @psalm-suppress DeprecatedMethod */ + } elseif ($this->metaModel->isTranslated()) { // @coverageIgnoreStart // @codingStandardsIgnoreStart @\trigger_error( @@ -255,12 +250,14 @@ private function determineJumpToInformation(): array ); // @codingStandardsIgnoreEnd $translated = true; - $desiredLanguage = $this->metaModel->getActiveLanguage(); + /** @psalm-suppress DeprecatedMethod */ + $desiredLanguage = $this->metaModel->getActiveLanguage(); + /** @psalm-suppress DeprecatedMethod */ $fallbackLanguage = $this->metaModel->getFallbackLanguage(); // @coverageIgnoreEnd } - $cacheKey = $desiredLanguage . '.' . $fallbackLanguage; + $cacheKey = ($desiredLanguage ?? '') . '.' . ($fallbackLanguage ?? ''); if (!isset($this->jumpToCache[$cacheKey])) { $this->jumpToCache[$cacheKey] = $this->lookupJumpTo($translated, $desiredLanguage, $fallbackLanguage); } @@ -282,13 +279,13 @@ private function lookupJumpTo(bool $translated, string $desired = null, string $ $jumpToPageId = ''; $filterSettingId = ''; foreach ((array) $this->get('jumpTo') as $jumpTo) { - $langCode = $jumpTo['langcode']; + $langCode = $jumpTo['langcode'] ?? null; // If either desired language or fallback, keep the result. if (!$translated || ($langCode === $desired) || ($langCode === $fallback)) { - $jumpToPageId = $jumpTo['value']; - $filterSettingId = $jumpTo['filter']; + $jumpToPageId = $jumpTo['value'] ?? ''; + $filterSettingId = (string) ($jumpTo['filter'] ?? ''); // If the desired language, break. - // Otherwise try to get the desired one until all have been evaluated. + // Otherwise, try to get the desired one until all have been evaluated. if (!$translated || ($desired === $jumpTo['langcode'])) { break; } @@ -306,7 +303,7 @@ private function lookupJumpTo(bool $translated, string $desired = null, string $ 'filter' => $filterSettingId, 'filterSetting' => $filterSetting, // Mask out the "all languages" language key (See #687). - 'language' => $pageDetails['language'], + 'language' => $pageDetails['language'] ?? '', 'label' => $this->getJumpToLabel() ]; } @@ -318,7 +315,7 @@ public function buildJumpToUrlFor(IItem $item) { $information = $this->determineJumpToInformation(); if (empty($information['pageDetails'])) { - return array(); + return []; } $result = $information; @@ -337,7 +334,7 @@ public function buildJumpToUrlFor(IItem $item) foreach ($parameterList as $strKey => $strValue) { // Sadly the filter values are currently encoded due to legacy reasons. // For MetaModels 3, they should be passed around decoded everywhere. - $filterUrl->setSlug($strKey, rawurldecode($strValue)); + $filterUrl->setSlug($strKey, \rawurldecode($strValue))->setGet($strKey, ''); } } diff --git a/src/Render/Setting/Events/CreateRenderSettingFactoryEvent.php b/src/Render/Setting/Events/CreateRenderSettingFactoryEvent.php index bd22f4d35..39a28a6d5 100644 --- a/src/Render/Setting/Events/CreateRenderSettingFactoryEvent.php +++ b/src/Render/Setting/Events/CreateRenderSettingFactoryEvent.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2022 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2022 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -21,7 +21,7 @@ namespace MetaModels\Render\Setting\Events; use MetaModels\Render\Setting\IRenderSettingFactory; -use Symfony\Component\EventDispatcher\Event; +use Symfony\Contracts\EventDispatcher\Event; /** * This event is triggered for every render setting factory instance that is created. diff --git a/src/Render/Setting/ICollection.php b/src/Render/Setting/ICollection.php index 81be3d9e5..94dfe37cb 100644 --- a/src/Render/Setting/ICollection.php +++ b/src/Render/Setting/ICollection.php @@ -71,7 +71,7 @@ public function setSetting($strAttributeName, $objSetting); /** * Retrieve the names of all columns getting rendered via this setting. * - * @return string[] + * @return list */ public function getSettingNames(); diff --git a/src/Render/Setting/IRenderSettingFactory.php b/src/Render/Setting/IRenderSettingFactory.php index f2f274cd5..b5a221698 100644 --- a/src/Render/Setting/IRenderSettingFactory.php +++ b/src/Render/Setting/IRenderSettingFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2019 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -35,7 +35,6 @@ interface IRenderSettingFactory extends IServiceContainerAware * Create a ICollection instance from the id. * * @param IMetaModel $metaModel The MetaModel for which to retrieve the render setting. - * * @param string $settingId The id of the ICollection. * * @return ICollection The instance or null if not found. diff --git a/src/Render/Setting/ISimple.php b/src/Render/Setting/ISimple.php index 0c1b4b218..9ceb568e2 100644 --- a/src/Render/Setting/ISimple.php +++ b/src/Render/Setting/ISimple.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -54,7 +55,6 @@ public function get($strName); * Set a base property in the settings object. * * @param string $strName The name of the setting to set. - * * @param mixed $varSetting The value to use. * * @return ISimple The setting itself. diff --git a/src/Render/Setting/RenderSettingFactory.php b/src/Render/Setting/RenderSettingFactory.php index ea6991c38..9a9a6ab2a 100644 --- a/src/Render/Setting/RenderSettingFactory.php +++ b/src/Render/Setting/RenderSettingFactory.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Sven Baumann * @author David Molineus * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -24,6 +24,7 @@ use Contao\StringUtil; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\Filter\FilterUrlBuilder; use MetaModels\Filter\Setting\IFilterSettingFactory; use MetaModels\IMetaModel; @@ -34,52 +35,57 @@ /** * This is the filter settings factory interface. + * + * @psalm-suppress DeprecatedInterface + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class RenderSettingFactory implements IRenderSettingFactory { /** - * The event dispatcher. + * The service container. * - * @var IMetaModelsServiceContainer + * @var IMetaModelsServiceContainer|null * * @deprecated The service container will get removed. + * + * @psalm-suppress DeprecatedInterface */ - private $serviceContainer; + private ?IMetaModelsServiceContainer $serviceContainer = null; /** * The database connection. * * @var Connection */ - private $database; + private Connection $database; /** * The event dispatcher. * * @var EventDispatcherInterface */ - private $eventDispatcher; + private EventDispatcherInterface $eventDispatcher; /** * The filter setting factory. * * @var IFilterSettingFactory */ - private $filterFactory; + private IFilterSettingFactory $filterFactory; /** * The already created render settings. * - * @var ICollection[] + * @var array> */ - private $renderSettings; + private array $renderSettings = []; /** * The filter URL builder. * * @var FilterUrlBuilder */ - private $filterUrlBuilder; + private FilterUrlBuilder $filterUrlBuilder; /** * Create a new instance. @@ -110,6 +116,8 @@ public function __construct( * @return RenderSettingFactory * * @deprecated The service container will get removed, use the symfony service container instead. + * + * @psalm-suppress DeprecatedInterface */ public function setServiceContainer(IMetaModelsServiceContainer $serviceContainer, $deprecationNotice = true) { @@ -121,6 +129,7 @@ public function setServiceContainer(IMetaModelsServiceContainer $serviceContaine ); // @codingStandardsIgnoreEnd } + /** @psalm-suppress DeprecatedProperty */ $this->serviceContainer = $serviceContainer; if ($this->eventDispatcher->hasListeners(MetaModelsEvents::RENDER_SETTING_FACTORY_CREATE)) { @@ -133,6 +142,10 @@ public function setServiceContainer(IMetaModelsServiceContainer $serviceContaine ); // @codingStandardsIgnoreEnd + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress DeprecatedProperty + */ $this->serviceContainer->getEventDispatcher()->dispatch( new CreateRenderSettingFactoryEvent($this), MetaModelsEvents::RENDER_SETTING_FACTORY_CREATE @@ -148,6 +161,8 @@ public function setServiceContainer(IMetaModelsServiceContainer $serviceContaine * @return IMetaModelsServiceContainer * * @deprecated The service container will get removed, use the symfony service container instead. + * + * @psalm-suppress DeprecatedInterface */ public function getServiceContainer() { @@ -157,6 +172,13 @@ public function getServiceContainer() E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd + + /** @psalm-suppress DeprecatedProperty */ + if (null === $this->serviceContainer) { + throw new \RuntimeException('Deprecated service container has not been set.'); + } + + /** @psalm-suppress DeprecatedProperty */ return $this->serviceContainer; } @@ -164,10 +186,10 @@ public function getServiceContainer() * Collect the attribute settings for the given render setting. * * @param IMetaModel $metaModel The MetaModel instance to retrieve the settings for. - * * @param ICollection $renderSetting The render setting. * * @return void + * @throws Exception */ public function collectAttributeSettings(IMetaModel $metaModel, $renderSetting) { @@ -180,10 +202,10 @@ public function collectAttributeSettings(IMetaModel $metaModel, $renderSetting) ->andWhere('t.enabled=1') ->orderBy('t.sorting') ->setParameter('pid', $renderSetting->get('id')) - ->execute(); + ->executeQuery(); - foreach ($attributeRows->fetchAll(\PDO::FETCH_ASSOC) as $attributeRow) { - $attribute = $metaModel->getAttributeById($attributeRow['attr_id']); + foreach ($attributeRows->fetchAllAssociative() as $attributeRow) { + $attribute = $metaModel->getAttributeById((int) $attributeRow['attr_id']); if (!$attribute) { continue; } @@ -206,12 +228,12 @@ public function collectAttributeSettings(IMetaModel $metaModel, $renderSetting) * Create a ICollection instance from the id. * * @param IMetaModel $metaModel The MetaModel for which to retrieve the render setting. + * @param string|int $settingId The id of the ICollection. * - * @param string $settingId The id of the ICollection. - * - * @return ICollection The instance or null if not found. + * @return ICollection The instance. + * @throws Exception */ - protected function internalCreateRenderSetting(IMetaModel $metaModel, $settingId) + protected function internalCreateRenderSetting(IMetaModel $metaModel, string|int $settingId) { $row = $this ->database @@ -221,12 +243,12 @@ protected function internalCreateRenderSetting(IMetaModel $metaModel, $settingId ->where('t.pid=:pid') ->andWhere('t.id=:id') ->setParameter('pid', $metaModel->get('id')) - ->setParameter('id', $settingId ?: 0) + ->setParameter('id', (int) $settingId ?: 0) ->setMaxResults(1) - ->execute() - ->fetch(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAssociative(); - if (!$row) { + if (false === $row) { $row = []; } @@ -238,7 +260,7 @@ protected function internalCreateRenderSetting(IMetaModel $metaModel, $settingId $this->filterUrlBuilder ); - if ($renderSetting->get('id')) { + if (null !== $renderSetting->get('id')) { $this->collectAttributeSettings($metaModel, $renderSetting); } @@ -252,7 +274,7 @@ public function createCollection(IMetaModel $metaModel, $settingId = '') { $tableName = $metaModel->getTableName(); if (!isset($this->renderSettings[$tableName])) { - $this->renderSettings[$tableName] = array(); + $this->renderSettings[$tableName] = []; } if (!isset($this->renderSettings[$tableName][$settingId])) { diff --git a/src/Render/Setting/Simple.php b/src/Render/Setting/Simple.php index 774f909fc..a6c8379f5 100644 --- a/src/Render/Setting/Simple.php +++ b/src/Render/Setting/Simple.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,8 @@ * @package MetaModels/core * @author Christian Schiffler * @author Sven Baumann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -32,21 +33,21 @@ class Simple implements ISimple * * @var array */ - protected $arrBase = array(); + protected $arrBase = []; /** * The parenting instance. * - * @var ICollection + * @var ICollection|null */ - protected $parent; + protected $parent = null; /** * Create a new instance. * * @param array $arrInformation The array that holds all base information for the new instance. */ - public function __construct($arrInformation = array()) + public function __construct($arrInformation = []) { foreach ($arrInformation as $strKey => $varValue) { $this->set($strKey, StringUtil::deserialize($varValue)); @@ -74,6 +75,10 @@ public function setParent($parent) */ public function getParent() { + if (null === $this->parent) { + throw new \RuntimeException('Parent has not been set'); + } + return $this->parent; } @@ -93,7 +98,6 @@ public function get($strName) * Set a base property in the settings object. * * @param string $strName The name of the setting to set. - * * @param mixed $varSetting The value to use. * * @return ISimple The setting itself. @@ -111,6 +115,6 @@ public function set($strName, $varSetting) */ public function getKeys() { - return array_keys($this->arrBase); + return \array_keys($this->arrBase); } } diff --git a/src/Render/Template.php b/src/Render/Template.php index 7aeaf08ea..a9a0fdaf3 100644 --- a/src/Render/Template.php +++ b/src/Render/Template.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -18,29 +18,36 @@ * @author Sven Baumann * @author David Molineus * @author Ingolf Steinhardt - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\Render; +use Contao\Config; +use Contao\BackendTemplate; use Contao\CoreBundle\Framework\Adapter; +use Contao\FrontendTemplate; use Contao\System; use Contao\TemplateLoader; use ContaoCommunityAlliance\DcGeneral\Contao\RequestScopeDeterminator; +use Exception; use MetaModels\Helper\ContaoController; +use RuntimeException; + use function array_key_exists; /** * Template class for MetaModels. - * In most aspects this behaves identically to the FrontendTemplate class from Contao but it differs in respect to + * In most aspects this behaves identically to the FrontendTemplate class from Contao, but it differs in respect to * format selection. * The format is being determined upon parsing and not upon instantiation. There is also an optional "fail on not * found" flag, which defaults to false and therefore one can parse the template and have zero output instead of * cluttering the frontend with exceptions. * * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) */ class Template { @@ -54,14 +61,14 @@ class Template /** * Parent template. * - * @var string + * @var string|null */ protected $strParent; /** * Default template. * - * @var string + * @var string|null */ protected $strDefault; @@ -70,33 +77,33 @@ class Template * * @var array */ - protected $arrData = array(); + protected $arrData = []; /** * Current output format. Only valid when within {@link MetaModelTemplate::parse()}. * * @var string */ - protected $strFormat = null; + protected $strFormat = ''; /** * Blocks. * * @var array */ - protected $arrBlocks = array(); + protected $arrBlocks = []; /** * Block names. * * @var array */ - protected $arrBlockNames = array(); + protected $arrBlockNames = []; /** * The template loader. * - * @var Adapter|Adapter|null Template loader adapter. + * @var Adapter|Adapter Template loader adapter. */ protected $templateLoader; @@ -117,17 +124,21 @@ class Template protected static $templatePathCache = []; /** - * Makes all protected methods from class Controller callable publically. + * Makes all protected methods from class Controller callable publicly. * * @param string $strMethod The method name. - * * @param array $arrArgs The parameters for the method. * * @return mixed */ public function __call($strMethod, $arrArgs) { - return call_user_func_array(array(ContaoController::getInstance(), $strMethod), $arrArgs); + if (isset($this->$strMethod) && \is_callable($this->$strMethod)) { + return \call_user_func_array($this->$strMethod, $arrArgs); + } + + /** @psalm-suppress DeprecatedClass */ + return \call_user_func_array(array(ContaoController::getInstance(), $strMethod), $arrArgs); } /** @@ -135,7 +146,7 @@ public function __call($strMethod, $arrArgs) * * @param string $strTemplate The name of the template file. * @param Adapter|Adapter|null $templateLoader Template loader adapter. - * @param RequestScopeDeterminator $scopeDeterminator Request scope determinator. + * @param RequestScopeDeterminator|null $scopeDeterminator Request scope determinator. */ public function __construct( $strTemplate = '', @@ -152,7 +163,8 @@ public function __construct( E_USER_DEPRECATED ); // @codingStandardsIgnoreEnd - $templateLoader = System::getContainer()->get('contao.framework')->getAdapter(TemplateLoader::class); + $templateLoader = System::getContainer()->get('contao.framework')?->getAdapter(TemplateLoader::class); + assert($templateLoader instanceof Adapter); } $this->templateLoader = $templateLoader; @@ -165,6 +177,7 @@ public function __construct( ); // @codingStandardsIgnoreEnd $scopeDeterminator = System::getContainer()->get('cca.dc-general.scope-matcher'); + assert($scopeDeterminator instanceof RequestScopeDeterminator); } $this->scopeDeterminator = $scopeDeterminator; } @@ -173,7 +186,6 @@ public function __construct( * Set an object property. * * @param string $strKey The name of the property. - * * @param mixed $varValue The value to set. * * @return void @@ -195,12 +207,12 @@ public function __set($strKey, $varValue) */ public function __get($strKey) { - if (array_key_exists($strKey, $this->arrData)) { + if (\array_key_exists($strKey, $this->arrData)) { return $this->arrData[$strKey]; } if (!empty($GLOBALS['TL_CONFIG']['debugMode'])) { - trigger_error($this->getName() . ': Undefined template variable: ' . $strKey, E_USER_WARNING); + \trigger_error($this->getName() . ': Undefined template variable: ' . $strKey, E_USER_WARNING); } return null; } @@ -265,12 +277,14 @@ public function getName() * Print all template variables to the screen using print_r. * * @return void + * + * @SuppressWarnings(PHPMD.DevelopmentCodeFragment) */ public function showTemplateVars() { echo "
    \n";
             // @codingStandardsIgnoreStart - We really want to keep this debug function here.
    -        print_r($this->arrData);
    +        \print_r($this->arrData);
             // @codingStandardsIgnoreEnd
             echo "
    \n"; } @@ -279,12 +293,16 @@ public function showTemplateVars() * Print all template variables to the screen using var_dump. * * @return void + * + * @SuppressWarnings(PHPMD.DevelopmentCodeFragment) + * + * @psalm-suppress ForbiddenCode */ public function dumpTemplateVars() { echo "
    \n";
             // @codingStandardsIgnoreStart - We really want to keep this debug function here.
    -        var_dump($this->arrData);
    +        \var_dump($this->arrData);
             // @codingStandardsIgnoreEnd
             echo "
    \n"; } @@ -293,33 +311,33 @@ public function dumpTemplateVars() * Find a particular template file and return its path. * * @param string $strTemplate Name of the template file. - * * @param string $strFormat The format to search for. - * * @param bool $blnFailIfNotFound Boolean flag telling if an Exception shall be thrown when the file can not * be found. * - * @throws \RuntimeException When the flag has been set and the file has not been found. + * @throws RuntimeException When the flag has been set and the file has not been found. * - * @return string + * @return string|null * * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ protected function getTemplate($strTemplate, $strFormat = 'html5', $blnFailIfNotFound = false) { - $strTemplate = basename($strTemplate); + $strTemplate = \basename($strTemplate); $strCustom = 'templates'; - // Check for a theme folder. - if ($this->scopeDeterminator->currentScopeIsFrontend()) { - $tmpDir = str_replace('../', '', $GLOBALS['objPage']->templateGroup); - if (!empty($tmpDir)) { + + // Check for a theme folder if scope frontend and a normal page. + if (isset($GLOBALS['objPage']) && $this->scopeDeterminator->currentScopeIsFrontend()) { + $tmpDir = \str_replace('../', '', (string) $GLOBALS['objPage']->templateGroup); + if ('' !== $tmpDir) { $strCustom = $tmpDir; } } - if (isset(self::$templatePathCache[$strTemplate][$strFormat]) - && array_key_exists($strCustom, self::$templatePathCache[$strTemplate][$strFormat]) + if ( + isset(self::$templatePathCache[$strTemplate][$strFormat]) + && \array_key_exists($strCustom, self::$templatePathCache[$strTemplate][$strFormat]) ) { return self::$templatePathCache[$strTemplate][$strFormat][$strCustom] !== false ? self::$templatePathCache[$strTemplate][$strFormat][$strCustom] @@ -327,6 +345,7 @@ protected function getTemplate($strTemplate, $strFormat = 'html5', $blnFailIfNot } try { + /** @psalm-suppress InternalMethod - the ContaoFramework class is internal, not the method usage. */ self::$templatePathCache[$strTemplate][$strFormat][$strCustom] = $this->templateLoader->getPath( $strTemplate, $strFormat, @@ -334,11 +353,11 @@ protected function getTemplate($strTemplate, $strFormat = 'html5', $blnFailIfNot ); return self::$templatePathCache[$strTemplate][$strFormat][$strCustom]; - } catch (\Exception $exception) { + } catch (Exception $exception) { self::$templatePathCache[$strTemplate][$strFormat][$strCustom] = false; if ($blnFailIfNotFound) { - throw new \RuntimeException( - sprintf('Could not find template %s.%s', $strTemplate, $strFormat), + throw new RuntimeException( + \sprintf('Could not find template %s.%s', $strTemplate, $strFormat), 1, $exception ); @@ -358,14 +377,15 @@ protected function getTemplate($strTemplate, $strFormat = 'html5', $blnFailIfNot */ protected function callParseTemplateHook() { - if (isset($GLOBALS['METAMODEL_HOOKS']['parseTemplate']) - && is_array($GLOBALS['METAMODEL_HOOKS']['parseTemplate']) + if ( + isset($GLOBALS['METAMODEL_HOOKS']['parseTemplate']) + && \is_array($GLOBALS['METAMODEL_HOOKS']['parseTemplate']) ) { foreach ($GLOBALS['METAMODEL_HOOKS']['parseTemplate'] as $callback) { [$strClass, $strMethod] = $callback; - $objCallback = (in_array('getInstance', get_class_methods($strClass))) - ? call_user_func(array($strClass, 'getInstance')) + $objCallback = (\in_array('getInstance', \get_class_methods($strClass))) + ? \call_user_func(array($strClass, 'getInstance')) : new $strClass(); $objCallback->$strMethod($this); @@ -377,7 +397,6 @@ protected function callParseTemplateHook() * Parse the template file and return it as string. * * @param string $strOutputFormat The desired output format. - * * @param boolean $blnFailIfNotFound If set to true, the template object will throw an exception if the template * can not be found. Defaults to false. * @@ -385,7 +404,7 @@ protected function callParseTemplateHook() */ public function parse($strOutputFormat, $blnFailIfNotFound = false) { - if ($this->strTemplate == '') { + if ($this->strTemplate === '') { return ''; } @@ -404,11 +423,11 @@ public function parse($strOutputFormat, $blnFailIfNotFound = false) while ($this->strParent !== null) { $strCurrent = $this->strParent; $strParent = $this->strDefault - ?: $this->getTemplate($this->strParent, $this->strFormat, $blnFailIfNotFound); + ?? $this->getTemplate($this->strParent, $this->strFormat, $blnFailIfNotFound); // Check if we have the template. - if (empty($strParent)) { - return sprintf( + if (null === $strParent) { + return \sprintf( 'Template %s.%s not found (it is maybe within a unreachable theme folder?).', $this->strParent, $this->strFormat @@ -419,25 +438,30 @@ public function parse($strOutputFormat, $blnFailIfNotFound = false) $this->strParent = null; $this->strDefault = null; - ob_start(); + \ob_start(); + assert(\is_file($strParent)); + /** @var string|null $this->strParent */ include($strParent); - // Capture the output of the root template + // Capture the output of the root template. if ($this->strParent === null) { - $strBuffer = ob_get_contents(); - } elseif ($this->strParent == $strCurrent) { + $strBuffer = \ob_get_contents(); + } elseif ($this->strParent === $strCurrent) { $this->strDefault = $this->getTemplate($this->strParent, $this->strFormat, $blnFailIfNotFound); } - ob_end_clean(); + \ob_end_clean(); } // Reset the internal arrays. $this->arrBlocks = []; // Add start and end markers in debug mode. - if (\Config::get('debugMode') && ('html5' === $this->strFormat)) { - $strRelPath = str_replace(TL_ROOT . '/', '', $this->getTemplate($this->strTemplate, $this->strFormat)); + if (Config::get('debugMode') && ('html5' === $this->strFormat)) { + $rootDir = System::getContainer()->getParameter('kernel.project_dir'); + assert(\is_string($rootDir)); + $strRelPath = + \str_replace($rootDir . '/', '', (string) $this->getTemplate($this->strTemplate, $this->strFormat)); $strBuffer = << $strBuffer @@ -446,7 +470,7 @@ public function parse($strOutputFormat, $blnFailIfNotFound = false) EOF; } - return $strBuffer; + return (string) $strBuffer; } /** @@ -465,9 +489,7 @@ protected function getFormat() * Static convenience method to perform the whole rendering within one line of code. * * @param string $strTemplate Name of the template file. - * * @param string $strOutputFormat The desired output format. - * * @param array $arrTplData The data to use in the template. * * @param bool $blnFailIfNotFound If set to true, the template object will throw an exception if the template @@ -512,50 +534,50 @@ public function parent() * * @return void * - * @throws \Exception If a child templates contains nested blocks. + * @throws Exception If a child templates contains nested blocks. */ public function block($strName) { $this->arrBlockNames[] = $strName; - // Root template + // Root template. if ($this->strParent === null) { - // Register the block name + // Register the block name. if (!isset($this->arrBlocks[$strName])) { $this->arrBlocks[$strName] = '[[TL_PARENT]]'; - } elseif (is_array($this->arrBlocks[$strName])) { + } elseif (\is_array($this->arrBlocks[$strName])) { // Combine the contents of the child blocks - $callback = function ($current, $parent) { - return str_replace('[[TL_PARENT]]', $parent, $current); + $callback = static function (string $current, string $parent): string { + return \str_replace('[[TL_PARENT]]', $parent, $current); }; - $this->arrBlocks[$strName] = array_reduce($this->arrBlocks[$strName], $callback, '[[TL_PARENT]]'); + $this->arrBlocks[$strName] = \array_reduce($this->arrBlocks[$strName], $callback, '[[TL_PARENT]]'); } - // Handle nested blocks - if ($this->arrBlocks[$strName] != '[[TL_PARENT]]') { - // Output everything before the first TL_PARENT tag - if (strpos($this->arrBlocks[$strName], '[[TL_PARENT]]') !== false) { - [$content] = explode('[[TL_PARENT]]', $this->arrBlocks[$strName], 2); + // Handle nested blocks. + if ($this->arrBlocks[$strName] !== '[[TL_PARENT]]') { + // Output everything before the first TL_PARENT tag. + if (\strpos($this->arrBlocks[$strName], '[[TL_PARENT]]') !== false) { + [$content] = \explode('[[TL_PARENT]]', $this->arrBlocks[$strName], 2); echo $content; } else { // Output the current block and start a new output buffer to remove the following blocks echo $this->arrBlocks[$strName]; - ob_start(); + \ob_start(); } } } else { // Child template - // Clean the output buffer - ob_end_clean(); + // Clean the output buffer. + \ob_end_clean(); - // Check for nested blocks - if (count($this->arrBlockNames) > 1) { - throw new \Exception('Nested blocks are not allowed in child templates'); + // Check for nested blocks. + if (\count($this->arrBlockNames) > 1) { + throw new Exception('Nested blocks are not allowed in child templates'); } - // Start a new output buffer - ob_start(); + // Start a new output buffer. + \ob_start(); } } @@ -564,62 +586,61 @@ public function block($strName) * * @return void * - * @throws \Exception If there is no open block. + * @throws Exception If there is no open block. */ public function endblock() { - // Check for open blocks + // Check for open blocks. if (empty($this->arrBlockNames)) { - throw new \Exception('You must start a block before you can end it'); + throw new Exception('You must start a block before you can end it'); } // Get the block name - $name = array_pop($this->arrBlockNames); + $name = \array_pop($this->arrBlockNames); - // Root template + // Root template. if ($this->strParent === null) { // Handle nested blocks - if ($this->arrBlocks[$name] != '[[TL_PARENT]]') { + if ($this->arrBlocks[$name] !== '[[TL_PARENT]]') { // Output everything after the first TL_PARENT tag - if (strpos($this->arrBlocks[$name], '[[TL_PARENT]]') !== false) { - [, $content] = explode('[[TL_PARENT]]', $this->arrBlocks[$name], 2); + if (\str_contains($this->arrBlocks[$name], '[[TL_PARENT]]')) { + [, $content] = \array_merge(\explode('[[TL_PARENT]]', $this->arrBlocks[$name], 2), ['']); echo $content; } else { // Remove the overwritten content - ob_end_clean(); + \ob_end_clean(); } } } else { // Child template - // Capture the block content - $this->arrBlocks[$name][] = ob_get_contents(); - ob_end_clean(); + // Capture the block content. + $this->arrBlocks[$name][] = \ob_get_clean(); // Start a new output buffer - ob_start(); + \ob_start(); } } /** * Insert a template * - * @param string $strName The template name. - * @param array $arrData An optional data array. + * @param string $strName The template name. + * @param array|null $arrData An optional data array. * * @return void */ public function insert($strName, array $arrData = null) { if ($this->scopeDeterminator->currentScopeIsBackend()) { - $objTemplate = new \BackendTemplate($strName); + $objTemplate = new BackendTemplate($strName); } else { - $objTemplate = new \FrontendTemplate($strName); + $objTemplate = new FrontendTemplate($strName); } if ($arrData !== null) { $objTemplate->setData($arrData); } - echo $objTemplate->parse($this->getFormat()); + echo $objTemplate->parse(); } } diff --git a/src/Schema/Doctrine/AbstractAttributeTypeSchemaGenerator.php b/src/Schema/Doctrine/AbstractAttributeTypeSchemaGenerator.php new file mode 100644 index 000000000..b3035a5aa --- /dev/null +++ b/src/Schema/Doctrine/AbstractAttributeTypeSchemaGenerator.php @@ -0,0 +1,67 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema\Doctrine; + +use Doctrine\DBAL\Schema\Table; +use MetaModels\Information\AttributeInformation; +use MetaModels\Information\MetaModelCollectionInterface; + +/** + * This abstract class eases schema generators for single types. + */ +abstract class AbstractAttributeTypeSchemaGenerator implements DoctrineSchemaGeneratorInterface +{ + use DoctrineSchemaGeneratorHelperTrait; + + /** + * {@inheritDoc} + */ + public function generate(DoctrineSchemaInformation $schema, MetaModelCollectionInterface $collection): void + { + $typeName = $this->getTypeName(); + foreach ($collection as $metaModelInformation) { + $attributes = $metaModelInformation->getAttributesOfType($typeName); + $tableSchema = $this->getSchemaForMetaModel($schema, $metaModelInformation); + foreach ($attributes as $attribute) { + assert($attribute instanceof AttributeInformation, 'Sorry, we mistyped the generateAttribute method.'); + $this->generateAttribute($tableSchema, $attribute); + } + } + } + + /** + * Generate an attribute. + * + * @param Table $tableSchema The table schema. + * @param AttributeInformation $attribute The attribute to generate. + * + * @return void + */ + abstract protected function generateAttribute(Table $tableSchema, AttributeInformation $attribute): void; + + /** + * Obtain the type name. + * + * @return string + */ + abstract protected function getTypeName(): string; +} diff --git a/src/Schema/Doctrine/DoctrineSchemaGenerator.php b/src/Schema/Doctrine/DoctrineSchemaGenerator.php new file mode 100644 index 000000000..7fb80c98b --- /dev/null +++ b/src/Schema/Doctrine/DoctrineSchemaGenerator.php @@ -0,0 +1,65 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema\Doctrine; + +use MetaModels\Information\MetaModelCollectionInterface; +use MetaModels\Schema\SchemaGeneratorInterface; +use MetaModels\Schema\SchemaInformation; + +/** + * This is the base for a schema generator working with doctrine schemas. + */ +class DoctrineSchemaGenerator implements SchemaGeneratorInterface +{ + /** + * The list of schema providers. + * + * @var DoctrineSchemaGeneratorInterface[] + */ + private $providers; + + /** + * Create a new instance. + * + * @param array $providers The schema providers. + */ + public function __construct(array $providers) + { + $this->providers = $providers; + } + + /** + * {@inheritDoc} + */ + public function generate(SchemaInformation $information, MetaModelCollectionInterface $collection): void + { + if (!$information->has(DoctrineSchemaInformation::class)) { + $information->add(new DoctrineSchemaInformation()); + } + /** @var DoctrineSchemaInformation $schema */ + $schema = $information->get(DoctrineSchemaInformation::class); + + foreach ($this->providers as $provider) { + $provider->generate($schema, $collection); + } + } +} diff --git a/src/Schema/Doctrine/DoctrineSchemaGeneratorHelperTrait.php b/src/Schema/Doctrine/DoctrineSchemaGeneratorHelperTrait.php new file mode 100644 index 000000000..924e495af --- /dev/null +++ b/src/Schema/Doctrine/DoctrineSchemaGeneratorHelperTrait.php @@ -0,0 +1,77 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema\Doctrine; + +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\Type; +use MetaModels\Information\MetaModelInformationInterface; + +/** + * This trait provides common used table manipulation tasks. + */ +trait DoctrineSchemaGeneratorHelperTrait +{ + /** + * Obtain the raw table schema for a MetaModel. + * + * @param DoctrineSchemaInformation $schema The schema to populate. + * @param MetaModelInformationInterface $metaModelInformation The MetaModel information. + * + * @return Table + */ + protected function getSchemaForMetaModel( + DoctrineSchemaInformation $schema, + MetaModelInformationInterface $metaModelInformation + ): Table { + $rawSchema = $schema->getSchema(); + if (!$rawSchema->hasTable($metaModelInformation->getName())) { + return $rawSchema->createTable($metaModelInformation->getName()); + } + + return $rawSchema->getTable($metaModelInformation->getName()); + } + + /** + * Update the information for a single column. + * + * @param Table $tableSchema The table schema. + * @param string $name The table name. + * @param string $typeName The type name. + * @param array $options The options. + * + * @return Column + */ + protected function setColumnData(Table $tableSchema, string $name, string $typeName, array $options = []): Column + { + if (!$tableSchema->hasColumn($name)) { + return $tableSchema->addColumn($name, $typeName, $options); + } + + $column = $tableSchema->getColumn($name); + $column->setType(Type::getType($typeName)); + if ([] !== $options) { + $column->setOptions($options); + } + return $column; + } +} diff --git a/src/Schema/Doctrine/DoctrineSchemaGeneratorInterface.php b/src/Schema/Doctrine/DoctrineSchemaGeneratorInterface.php new file mode 100644 index 000000000..bf959afe7 --- /dev/null +++ b/src/Schema/Doctrine/DoctrineSchemaGeneratorInterface.php @@ -0,0 +1,40 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema\Doctrine; + +use MetaModels\Information\MetaModelCollectionInterface; + +/** + * This interface describes a schema provider. + */ +interface DoctrineSchemaGeneratorInterface +{ + /** + * Generate a schema. + * + * @param DoctrineSchemaInformation $schema The doctrine schema to populate. + * @param MetaModelCollectionInterface $collection The collection of MetaModels. + * + * @return void + */ + public function generate(DoctrineSchemaInformation $schema, MetaModelCollectionInterface $collection): void; +} diff --git a/src/Schema/Doctrine/DoctrineSchemaInformation.php b/src/Schema/Doctrine/DoctrineSchemaInformation.php new file mode 100644 index 000000000..c788fab42 --- /dev/null +++ b/src/Schema/Doctrine/DoctrineSchemaInformation.php @@ -0,0 +1,134 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema\Doctrine; + +use Doctrine\DBAL\Schema\Schema; +use MetaModels\Schema\SchemaInformationInterface; + +/** + * This is the encapsulation of doctrine schema information. + */ +class DoctrineSchemaInformation implements SchemaInformationInterface +{ + /** + * The generated doctrine schema. + */ + private Schema $schema; + + /** + * A list of pre-processors ordered by priority. + * + * @var array> + */ + private array $preProcessors = []; + + /** + * A list of post processors by priority. + * + * @var array> + */ + private array $postProcessors = []; + + /** + * Create a new instance. + * + * @param Schema|null $schema The contained doctrine schema. + */ + public function __construct(?Schema $schema = null) + { + $this->schema = ($schema ?? new Schema()); + } + + /** + * {@inheritDoc} + */ + public function getName(): string + { + return static::class; + } + + /** + * Retrieve schema. + */ + public function getSchema(): Schema + { + return $this->schema; + } + + /** + * Add a pre processor. + * + * @param SchemaProcessorInterface $processor The processor to add. + * @param int $priority The priority to use. + */ + public function addPreProcessor(SchemaProcessorInterface $processor, int $priority = 0): void + { + if (!isset($this->preProcessors[$priority])) { + $this->preProcessors[$priority] = []; + krsort($this->preProcessors); + } + $this->preProcessors[$priority][] = $processor; + } + + /** + * Retrieve preProcessors. + * + * @return list + */ + public function getPreProcessors(): array + { + if ([] === $this->preProcessors) { + return []; + } + + return array_merge(...$this->preProcessors); + } + + /** + * Add a post processor. + * + * @param SchemaProcessorInterface $processor The processor to add. + * @param int $priority The priority to use. + */ + public function addPostProcessor(SchemaProcessorInterface $processor, int $priority = 0): void + { + if (!isset($this->postProcessors[$priority])) { + $this->postProcessors[$priority] = []; + krsort($this->postProcessors); + } + $this->postProcessors[$priority][] = $processor; + } + + /** + * Retrieve postProcessors. + * + * @return list + */ + public function getPostProcessors(): array + { + if ([] === $this->postProcessors) { + return []; + } + + return array_merge(...$this->postProcessors); + } +} diff --git a/src/Schema/Doctrine/DoctrineSchemaManager.php b/src/Schema/Doctrine/DoctrineSchemaManager.php new file mode 100644 index 000000000..3573079d1 --- /dev/null +++ b/src/Schema/Doctrine/DoctrineSchemaManager.php @@ -0,0 +1,139 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema\Doctrine; + +use MetaModels\Schema\SchemaInformation; +use MetaModels\Schema\SchemaManagerInterface; + +/** + * This is the base for a schema manager working with doctrine schemas. + */ +class DoctrineSchemaManager implements SchemaManagerInterface +{ + /** + * The schema manipulator. + * + * @var DoctrineSchemaManipulator + */ + private $manipulator; + + /** + * Create a new instance. + * + * @param DoctrineSchemaManipulator $manipulator The database connection. + */ + public function __construct(DoctrineSchemaManipulator $manipulator) + { + $this->manipulator = $manipulator; + } + + /** + * Pre-process the schema information. + * + * @param SchemaInformation $information The schema information. + * + * @return void + */ + public function preprocess(SchemaInformation $information): void + { + // If no information added, exit. + if (!$information->has(DoctrineSchemaInformation::class)) { + return; + } + /** @var DoctrineSchemaInformation $doctrine */ + $doctrine = $information->get(DoctrineSchemaInformation::class); + + foreach ($doctrine->getPreProcessors() as $preProcessor) { + $preProcessor->process(); + } + } + + /** + * Pre-process the schema information. + * + * @param SchemaInformation $information The schema information. + * + * @return void + */ + public function process(SchemaInformation $information): void + { + // If no information added, exit. + if (!$information->has(DoctrineSchemaInformation::class)) { + return; + } + + /** @var DoctrineSchemaInformation $doctrine */ + $doctrine = $information->get(DoctrineSchemaInformation::class); + $this->manipulator->updateDatabase($doctrine); + } + + /** + * Post-process the schema information. + * + * @param SchemaInformation $information The schema information. + * + * @return void + */ + public function postprocess(SchemaInformation $information): void + { + // If no information added, exit. + if (!$information->has(DoctrineSchemaInformation::class)) { + return; + } + + /** @var DoctrineSchemaInformation $doctrine */ + $doctrine = $information->get(DoctrineSchemaInformation::class); + + foreach ($doctrine->getPostProcessors() as $postProcessor) { + $postProcessor->process(); + } + } + + /** + * {@inheritDoc} + */ + public function validate(SchemaInformation $information): array + { + // If no information added, exit. + if (!$information->has(DoctrineSchemaInformation::class)) { + return []; + } + + $tasks = []; + /** @var DoctrineSchemaInformation $doctrine */ + $doctrine = $information->get(DoctrineSchemaInformation::class); + + $tasks[] = array_map(function ($preProcessor) { + return (string) $preProcessor; + }, $doctrine->getPreProcessors()); + + $tasks[1] = array_map(function (string $query) { + return 'Execute SQL: ' . $query; + }, $this->manipulator->getScript($doctrine)); + + $tasks[] = array_map(function ($postProcessor) { + return (string) $postProcessor; + }, $doctrine->getPostProcessors()); + + return array_merge(...$tasks); + } +} diff --git a/src/Schema/Doctrine/DoctrineSchemaManipulator.php b/src/Schema/Doctrine/DoctrineSchemaManipulator.php new file mode 100644 index 000000000..21c1bf2f5 --- /dev/null +++ b/src/Schema/Doctrine/DoctrineSchemaManipulator.php @@ -0,0 +1,167 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema\Doctrine; + +use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; +use Doctrine\DBAL\Schema\AbstractSchemaManager; +use Doctrine\DBAL\Schema\Column; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\SchemaDiff; +use Doctrine\DBAL\Types\Type; +use Doctrine\DBAL\Types\Types; + +use function in_array; + +/** + * This updates the database to be in sync with the passed schema. + */ +class DoctrineSchemaManipulator +{ + /** + * The doctrine connection. + * + * @var Connection + */ + private Connection $connection; + + /** + * Create a new instance. + * + * @param Connection $connection The database connection to use. + */ + public function __construct(Connection $connection) + { + $this->connection = $connection; + } + + /** + * Update the database to be in sync with the passed doctrine information. + * + * @param DoctrineSchemaInformation $schemaInformation The schema information. + * + * @return void + */ + public function updateDatabase(DoctrineSchemaInformation $schemaInformation): void + { + foreach ($this->buildChangeSet($schemaInformation) as $query) { + $this->connection->executeStatement($query); + } + } + + /** + * Obtain the list of tasks being performed. + * + * @param DoctrineSchemaInformation $schemaInformation The schema information. + * + * @return string[] + */ + public function getScript(DoctrineSchemaInformation $schemaInformation): array + { + return $this->buildChangeSet($schemaInformation); + } + + /** + * Build the change set. + * + * @param DoctrineSchemaInformation $schemaInformation The schema information. + * + * @return list + * @throws Exception + * + * @psalm-suppress InternalProperty + */ + private function buildChangeSet(DoctrineSchemaInformation $schemaInformation): array + { + $platform = $this->connection->getDatabasePlatform(); + $manager = $this->connection->createSchemaManager(); + $current = $manager->introspectSchema(); + $diff = $this->diff($current, $schemaInformation->getSchema(), $manager); + + foreach ($diff->changedTables as $changedTable) { + foreach ($changedTable->removedColumns as $removedColumn) { + // @codingStandardsIgnoreStart + @trigger_error( + 'Ignoring drop of column "' . $removedColumn->getName(), + E_USER_WARNING + ); + // @codingStandardsIgnoreEnd + } + $changedTable->removedColumns = []; + } + foreach ($diff->removedTables as $removedTable) { + // @codingStandardsIgnoreStart + @trigger_error( + 'Ignoring drop of table "' . $removedTable->getName(), + E_USER_WARNING + ); + // @codingStandardsIgnoreEnd + } + $diff->removedTables = []; + + return $platform->getAlterSchemaSQL($diff); + } + + private function diff(Schema $current, Schema $desired, AbstractSchemaManager $manager): SchemaDiff + { + // We have to "inherit" collation and charset for certain types as doctrine will report them in the current + // columns and always mark them as changed when no charset/collation has been explicitly specified in the + // desired column - despite being already in the correct condition "on disk". + $checkTypes = [ + Types::ASCII_STRING, + Types::STRING, + Types::TEXT, + ]; + + $registry = Type::getTypeRegistry(); + foreach ($desired->getTables() as $table) { + if (!$current->hasTable($table->getName())) { + continue; + } + $existingTable = $current->getTable($table->getName()); + foreach ($table->getColumns() as $column) { + if (!$existingTable->hasColumn($column->getName())) { + continue; + } + $existingColumn = $existingTable->getColumn($column->getName()); + if (!in_array($registry->lookupName($column->getType()), $checkTypes, true)) { + continue; + } + $this->inheritPlatformOptionIfNotSet('collation', $column, $existingColumn); + $this->inheritPlatformOptionIfNotSet('charset', $column, $existingColumn); + } + } + + return $manager->createComparator()->compareSchemas($current, $desired); + } + + private function inheritPlatformOptionIfNotSet(string $optionName, Column $column, Column $existingColumn): void + { + if (!$column->hasPlatformOption($optionName)) { + if (!$existingColumn->hasPlatformOption($optionName)) { + return; + } + $column->setPlatformOption($optionName, $existingColumn->getPlatformOption($optionName)); + } + } +} diff --git a/src/Schema/Doctrine/SchemaProcessorInterface.php b/src/Schema/Doctrine/SchemaProcessorInterface.php new file mode 100644 index 000000000..a938539cf --- /dev/null +++ b/src/Schema/Doctrine/SchemaProcessorInterface.php @@ -0,0 +1,42 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema\Doctrine; + +/** + * This interface describes a schema processor for pre/post processing. + */ +interface SchemaProcessorInterface +{ + /** + * Perform the action. + * + * @return void + */ + public function process(): void; + + /** + * Get a string representation of the task performed. + * + * @return string + */ + public function __toString(); +} diff --git a/src/Schema/Doctrine/SystemColumnSchemaGenerator.php b/src/Schema/Doctrine/SystemColumnSchemaGenerator.php new file mode 100644 index 000000000..02eae5fb7 --- /dev/null +++ b/src/Schema/Doctrine/SystemColumnSchemaGenerator.php @@ -0,0 +1,93 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema\Doctrine; + +use Doctrine\DBAL\Types\Types; +use MetaModels\Information\MetaModelCollectionInterface; +use MetaModels\Information\MetaModelInformationInterface; + +/** + * This interface describes a schema provider. + */ +class SystemColumnSchemaGenerator implements DoctrineSchemaGeneratorInterface +{ + use DoctrineSchemaGeneratorHelperTrait; + + /** + * {@inheritDoc} + */ + public function generate(DoctrineSchemaInformation $schema, MetaModelCollectionInterface $collection): void + { + foreach ($collection as $metaModelInformation) { + $this->generateMetaModelSchema($schema, $metaModelInformation); + } + } + + /** + * Generate the schema for a MetaModel. + * + * @param DoctrineSchemaInformation $schema The doctrine schema to populate. + * @param MetaModelInformationInterface $metaModelInformation The metamodel information to use. + */ + private function generateMetaModelSchema( + DoctrineSchemaInformation $schema, + MetaModelInformationInterface $metaModelInformation + ): void { + $tableSchema = $this->getSchemaForMetaModel($schema, $metaModelInformation); + + $this->setColumnData($tableSchema, 'id', Types::INTEGER, [ + 'unsigned' => true, + 'notnull' => true, + 'autoincrement' => true, + ]); + $this->setColumnData($tableSchema, 'pid', Types::INTEGER, [ + 'unsigned' => true, + 'notnull' => true, + 'default' => 0, + ]); + $this->setColumnData($tableSchema, 'sorting', Types::INTEGER, [ + 'unsigned' => true, + 'default' => 0, + ]); + $this->setColumnData($tableSchema, 'tstamp', Types::INTEGER, [ + 'unsigned' => true, + 'default' => 0, + ]); + + $tableSchema->setPrimaryKey(['id']); + + if ( + $metaModelInformation->hasConfigurationValue('varsupport') + && '1' === $metaModelInformation->getConfigurationValue('varsupport') + ) { + $this->setColumnData($tableSchema, 'varbase', Types::STRING, [ + 'length' => 1, + 'fixed' => true, + 'default' => '', + ]); + $this->setColumnData($tableSchema, 'vargroup', Types::INTEGER, [ + 'unsigned' => true, + 'default' => 0, + ]); + } + } +} diff --git a/src/Schema/LegacySchemaGenerator.php b/src/Schema/LegacySchemaGenerator.php new file mode 100644 index 000000000..a7c60e6a2 --- /dev/null +++ b/src/Schema/LegacySchemaGenerator.php @@ -0,0 +1,101 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema; + +use MetaModels\Attribute\IInternal; +use MetaModels\IFactory; +use MetaModels\IMetaModel; +use MetaModels\Information\MetaModelCollectionInterface; + +use function in_array; + +/** + * This is the legacy handler for creating the legacy schema. + * + * @deprecated Only used as bc layer for 2.0 - to be removed in 3.0. + */ +class LegacySchemaGenerator implements SchemaGeneratorInterface +{ + /** @var list */ + private array $ignoredTypeNames; + + /** + * The MetaModels factory. + * + * @var IFactory + */ + private IFactory $factory; + + /** + * Create a new instance. + * + * @param IFactory $factory The factory. + * @param list $ignoredTypeNames + */ + public function __construct(IFactory $factory, array $ignoredTypeNames) + { + $this->factory = $factory; + $this->ignoredTypeNames = $ignoredTypeNames; + } + + /** + * {@inheritDoc} + */ + public function generate(SchemaInformation $information, MetaModelCollectionInterface $collection): void + { + /** @psalm-suppress DeprecatedClass */ + if (!$information->has(LegacySchemaInformation::class)) { + /** @psalm-suppress DeprecatedClass */ + $information->add(new LegacySchemaInformation()); + } + + /** + * @var LegacySchemaInformation $legacy + * @psalm-suppress DeprecatedClass + */ + $legacy = $information->get(LegacySchemaInformation::class); + + foreach ($collection as $metaModelInformation) { + $metaModel = $this->factory->getMetaModel($metaModelInformation->getName()); + assert($metaModel instanceof IMetaModel); + foreach ($metaModel->getAttributes() as $attribute) { + // Skip managed and internal attributes. + if ( + $attribute instanceof IInternal + || \in_array($attribute->get('type'), $this->ignoredTypeNames, true) + ) { + continue; + } + + // @codingStandardsIgnoreStart + @trigger_error( + 'Attribute type "' . $attribute->get('type') . '" should be changed to a managed attribute.', + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + + $legacy->addAttribute($attribute); + } + } + } +} diff --git a/src/Schema/LegacySchemaInformation.php b/src/Schema/LegacySchemaInformation.php new file mode 100644 index 000000000..c1b8e40a0 --- /dev/null +++ b/src/Schema/LegacySchemaInformation.php @@ -0,0 +1,69 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema; + +use MetaModels\Attribute\IAttribute; + +/** + * This schema information is used for bc to MetaModels 2.0 interfaces. + * + * @deprecated Since 2.1 - to be removed in 3.0 + */ +class LegacySchemaInformation implements SchemaInformationInterface +{ + /** + * The attributes. + * + * @var IAttribute[] + */ + private $attributes = []; + + /** + * {@inheritDoc} + */ + public function getName(): string + { + return static::class; + } + + /** + * Add an attribute + * + * @param IAttribute $attribute The attribute to add. + * + * @return void + */ + public function addAttribute(IAttribute $attribute): void + { + $this->attributes[] = $attribute; + } + + /** + * Retrieve attributes. + * + * @return IAttribute[] + */ + public function getAttributes(): array + { + return $this->attributes; + } +} diff --git a/src/Schema/LegacySchemaManager.php b/src/Schema/LegacySchemaManager.php new file mode 100644 index 000000000..d7e1fcbbf --- /dev/null +++ b/src/Schema/LegacySchemaManager.php @@ -0,0 +1,132 @@ + + * @author Ingolf Steinhardt + * @copyright 2012-2023 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema; + +use Doctrine\DBAL\Exception\NonUniqueFieldNameException; + +/** + * This is the legacy schema manager used for bc reasons. + * + * @deprecated Since 2.1 - to be removed in 3.0 + */ +class LegacySchemaManager implements SchemaManagerInterface +{ + /** + * {@inheritDoc} + */ + public function preprocess(SchemaInformation $information): void + { + // No-op. + } + + /** + * {@inheritDoc} + */ + public function process(SchemaInformation $information): void + { + /** @psalm-suppress DeprecatedClass */ + if (!$information->has(LegacySchemaInformation::class)) { + return; + } + /** + * @var LegacySchemaInformation $legacySchema + * @psalm-suppress DeprecatedClass + */ + $legacySchema = $information->get(LegacySchemaInformation::class); + + foreach ($legacySchema->getAttributes() as $attribute) { + try { + /** @psalm-suppress DeprecatedMethod */ + $attribute->initializeAUX(); + } catch (\Throwable $exception) { + // Transcribe known exceptions. + switch (true) { + case $exception instanceof NonUniqueFieldNameException: + if ( + \preg_match( + '/SQLSTATE\[42S21\]: ' . + 'Column already exists: 1060 Duplicate column name \'(?P.+)\'/', + $exception->getMessage(), + $matches + ) + ) { + // @codingStandardsIgnoreStart + @trigger_error('Column already exists: "' . $matches['name'] . '"'); + // @codingStandardsIgnoreEnd + continue 2; + } + break; + default: + } + + // @codingStandardsIgnoreStart + @trigger_error( + \sprintf( + 'Ignored exception "%1$s" for attribute "%2$s": %3$s', + \get_class($exception), + $attribute->getColName(), + $exception->getMessage() + ), + E_USER_DEPRECATED + ); + // @codingStandardsIgnoreEnd + } + } + } + + /** + * {@inheritDoc} + */ + public function postprocess(SchemaInformation $information): void + { + // No-op. + } + + /** + * {@inheritDoc} + */ + public function validate(SchemaInformation $information): array + { + /** @psalm-suppress DeprecatedClass */ + if (!$information->has(LegacySchemaInformation::class)) { + return []; + } + + /** + * @var LegacySchemaInformation $legacySchema + * @psalm-suppress DeprecatedClass + */ + $legacySchema = $information->get(LegacySchemaInformation::class); + + $tasks = []; + foreach ($legacySchema->getAttributes() as $attribute) { + $tasks[] = \sprintf( + '(Re-)Initialize attribute "%1$s" (type: "%2$s") via legacy method.', + $attribute->getColName(), + $attribute->get('type') + ); + } + + return $tasks; + } +} diff --git a/src/Schema/SchemaGenerator.php b/src/Schema/SchemaGenerator.php new file mode 100644 index 000000000..1dcb56f02 --- /dev/null +++ b/src/Schema/SchemaGenerator.php @@ -0,0 +1,57 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema; + +use MetaModels\Information\MetaModelCollectionInterface; + +/** + * This is the main MetaModels schema generator, it is a composite of other generators. + */ +class SchemaGenerator implements SchemaGeneratorInterface +{ + /** + * The list of registered schema managers. + * + * @var SchemaGeneratorInterface[] + */ + private $generators; + + /** + * Create a new instance. + * + * @param SchemaGeneratorInterface[] $generators The managers to use. + */ + public function __construct(array $generators) + { + $this->generators = $generators; + } + + /** + * {@inheritDoc} + */ + public function generate(SchemaInformation $information, MetaModelCollectionInterface $collection): void + { + foreach ($this->generators as $manager) { + $manager->generate($information, $collection); + } + } +} diff --git a/src/Schema/SchemaGeneratorInterface.php b/src/Schema/SchemaGeneratorInterface.php new file mode 100644 index 000000000..232a608bb --- /dev/null +++ b/src/Schema/SchemaGeneratorInterface.php @@ -0,0 +1,40 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema; + +use MetaModels\Information\MetaModelCollectionInterface; + +/** + * This interface describes a generic schema generator. + */ +interface SchemaGeneratorInterface +{ + /** + * Generate the schema information. + * + * @param SchemaInformation $information The schema information. + * @param MetaModelCollectionInterface $collection The collection of MetaModels. + * + * @return void + */ + public function generate(SchemaInformation $information, MetaModelCollectionInterface $collection): void; +} diff --git a/src/Schema/SchemaInformation.php b/src/Schema/SchemaInformation.php new file mode 100644 index 000000000..4e1e60d44 --- /dev/null +++ b/src/Schema/SchemaInformation.php @@ -0,0 +1,91 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema; + +use InvalidArgumentException; + +/** + * This encapsulates the different schema engine information. + */ +class SchemaInformation +{ + /** + * The list of registered schema information. + * + * @var array + */ + private array $information = []; + + /** + * Retrieve schema information. + * + * @param string $name The information to retrieve the schema for. + * + * @return SchemaInformationInterface + * + * @throws InvalidArgumentException When the information is not registered. + */ + public function get(string $name): SchemaInformationInterface + { + if (!$this->has($name)) { + throw new InvalidArgumentException('Information with name "' . $name . '" not registered.'); + } + + return $this->information[$name]; + } + + /** + * Test if the information with the passed name is registered. + * + * @param string $name The name of the information to search. + */ + public function has(string $name): bool + { + return isset($this->information[$name]); + } + + /** + * Test if the information with the passed name is registered. + * + * @param SchemaInformationInterface $information The information to add. + * + * @throws InvalidArgumentException When the information is already registered. + */ + public function add(SchemaInformationInterface $information): void + { + if ($this->has($name = $information->getName())) { + throw new InvalidArgumentException('Information with name "' . $name . '" already registered.'); + } + + $this->information[$information->getName()] = $information; + } + + /** + * Obtain the list of registered names. + * + * @return list + */ + public function getRegisteredNames(): array + { + return array_keys($this->information); + } +} diff --git a/src/Schema/SchemaInformationInterface.php b/src/Schema/SchemaInformationInterface.php new file mode 100644 index 000000000..c4cd595d3 --- /dev/null +++ b/src/Schema/SchemaInformationInterface.php @@ -0,0 +1,35 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema; + +/** + * This interface describes a MetaModels schema information for a particular engine. + */ +interface SchemaInformationInterface +{ + /** + * Obtain the name of the schema information. + * + * @return string + */ + public function getName(): string; +} diff --git a/src/Schema/SchemaManager.php b/src/Schema/SchemaManager.php new file mode 100644 index 000000000..0ecd506e2 --- /dev/null +++ b/src/Schema/SchemaManager.php @@ -0,0 +1,91 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema; + +/** + * This is the main MetaModels schema manager, it is a composite of other managers. + */ +class SchemaManager implements SchemaManagerInterface +{ + /** + * The list of registered schema managers. + * + * @var SchemaManagerInterface[] + */ + private array $managers; + + /** + * Create a new instance. + * + * @param SchemaManagerInterface[] $managers The managers to use. + */ + public function __construct(array $managers) + { + $this->managers = $managers; + } + + /** + * {@inheritDoc} + */ + public function preprocess(SchemaInformation $information): void + { + // pre process - this may perform data migrations and the like. + foreach ($this->managers as $manager) { + $manager->preprocess($information); + } + } + + /** + * {@inheritDoc} + */ + public function process(SchemaInformation $information): void + { + // process - here the automatic adjustments to the db will be made. + foreach ($this->managers as $manager) { + $manager->process($information); + } + } + + /** + * {@inheritDoc} + */ + public function postprocess(SchemaInformation $information): void + { + // post process - perform any cleanup to be done. + foreach ($this->managers as $manager) { + $manager->postprocess($information); + } + } + + /** + * {@inheritDoc} + */ + public function validate(SchemaInformation $information): array + { + $tasks = []; + foreach ($this->managers as $manager) { + $tasks[] = $manager->validate($information); + } + + return array_merge(...$tasks); + } +} diff --git a/src/Schema/SchemaManagerInterface.php b/src/Schema/SchemaManagerInterface.php new file mode 100644 index 000000000..636f65f88 --- /dev/null +++ b/src/Schema/SchemaManagerInterface.php @@ -0,0 +1,64 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Schema; + +/** + * This interface describes a schema manager. + */ +interface SchemaManagerInterface +{ + /** + * Pre-process the schema information. + * + * @param SchemaInformation $information The schema information. + * + * @return void + */ + public function preprocess(SchemaInformation $information): void; + + /** + * Pre-process the schema information. + * + * @param SchemaInformation $information The schema information. + * + * @return void + */ + public function process(SchemaInformation $information): void; + + /** + * Pre-process the schema information. + * + * @param SchemaInformation $information The schema information. + * + * @return void + */ + public function postprocess(SchemaInformation $information): void; + + /** + * List the tasks that will be performed for the passed information + * + * @param SchemaInformation $information The schema information. + * + * @return string[] + */ + public function validate(SchemaInformation $information): array; +} diff --git a/src/TranslatedMetaModel.php b/src/TranslatedMetaModel.php index 947c2d32f..aa0f3d131 100644 --- a/src/TranslatedMetaModel.php +++ b/src/TranslatedMetaModel.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -13,7 +13,7 @@ * @package MetaModels/core * @author Christian Schiffler * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,10 +22,13 @@ use Doctrine\DBAL\Connection; use MetaModels\Attribute\ITranslated; +use MetaModels\Helper\LocaleUtil; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** * This defines a translated MetaModel. + * + * @psalm-suppress PropertyNotSetInConstructor */ class TranslatedMetaModel extends MetaModel implements ITranslatedMetaModel { @@ -34,14 +37,14 @@ class TranslatedMetaModel extends MetaModel implements ITranslatedMetaModel * * @var string */ - private $activeLanguage; + private string $activeLanguage = ''; /** * The fallback language. * * @var string */ - private $mainLanguage; + private string $mainLanguage = ''; /** * The locale territory support. @@ -76,7 +79,7 @@ public function __construct($arrData, EventDispatcherInterface $dispatcher, Conn // Mark fallback language as active language. $this->activeLanguage = $this->mainLanguage; - $this->hasTerritorySupport = (bool) $this->arrData['localeterritorysupport']; + $this->hasTerritorySupport = (bool) ($this->arrData['localeterritorysupport'] ?? false); } /** @@ -84,7 +87,7 @@ public function __construct($arrData, EventDispatcherInterface $dispatcher, Conn */ public function getLanguages(): array { - return array_keys((array) $this->arrData['languages']); + return \array_keys((array) $this->arrData['languages']); } /** @@ -132,19 +135,21 @@ public function selectLanguage(string $activeLanguage): string */ protected function fetchTranslatedAttributeValues(ITranslated $attribute, $ids) { - $originalLanguage = $GLOBALS['TL_LANGUAGE']; - $GLOBALS['TL_LANGUAGE'] = \str_replace('_', '-', $this->getLanguage()); + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + $originalLanguage = LocaleUtil::formatAsLocale($GLOBALS['TL_LANGUAGE']); + $GLOBALS['TL_LANGUAGE'] = LocaleUtil::formatAsLanguageTag($this->getLanguage()); try { $attributeData = $attribute->getTranslatedDataFor($ids, $this->getLanguage()); // Second round, fetch missing data from main language. - if ([] !== $missing = array_diff($ids, array_keys($attributeData))) { + if ([] !== $missing = \array_values(\array_diff($ids, \array_keys($attributeData)))) { $attributeData += $attribute->getTranslatedDataFor($missing, $this->getMainLanguage()); } return $attributeData; } finally { - $GLOBALS['TL_LANGUAGE'] = $originalLanguage; + // @deprecated usage of TL_LANGUAGE - remove for Contao 5.0. + $GLOBALS['TL_LANGUAGE'] = LocaleUtil::formatAsLanguageTag($originalLanguage); } } } diff --git a/src/ViewCombination/InputScreenInformationBuilder.php b/src/ViewCombination/InputScreenInformationBuilder.php index 4c592379c..620d1c9de 100644 --- a/src/ViewCombination/InputScreenInformationBuilder.php +++ b/src/ViewCombination/InputScreenInformationBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author David Molineus * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -23,7 +23,9 @@ namespace MetaModels\ViewCombination; use Contao\StringUtil; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; +use Doctrine\DBAL\Exception; use MetaModels\IFactory; use MetaModels\IMetaModel; use MetaModels\ITranslatedMetaModel; @@ -38,14 +40,14 @@ class InputScreenInformationBuilder * * @var Connection */ - private $connection; + private Connection $connection; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Create a new instance. @@ -59,29 +61,59 @@ public function __construct(Connection $connection, IFactory $factory) $this->factory = $factory; } + /** + * Fetch information about all input screens. + * + * @return array + * + * @throws Exception + */ + public function fetchAllInputScreensForTable(string $tableName): array + { + $builder = $this->connection->createQueryBuilder(); + $screens = $builder + ->select('d.*') + ->from('tl_metamodel_dca', 'd') + ->leftJoin('d', 'tl_metamodel', 'm', 'm.id=d.pid') + ->where($builder->expr()->in('m.tableName', ':tableName')) + ->setParameter('tableName', $tableName) + ->orderBy('m.sorting') + ->executeQuery() + ->fetchAllAssociative(); + + $result = []; + foreach ($screens as $screen) { + $result[$screen['id']] = $this->prepareInputScreen($tableName, $screen); + // FIXME: simplify prepareInputScreen to not translate the values inline but define translation keys. + } + + return $result; + } + /** * Fetch information about an input screen. * * @param array $idList The ids of the input screens to obtain (table name => id). * * @return array + * @throws Exception */ public function fetchInputScreens($idList): array { - $idList = array_filter($idList); + $idList = \array_filter($idList); $builder = $this->connection->createQueryBuilder(); $screens = $builder ->select('d.*') ->from('tl_metamodel_dca', 'd') ->leftJoin('d', 'tl_metamodel', 'm', 'm.id=d.pid') ->where($builder->expr()->in('d.id', ':idList')) - ->setParameter('idList', $idList, Connection::PARAM_STR_ARRAY) + ->setParameter('idList', $idList, ArrayParameterType::STRING) ->orderBy('m.sorting') - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); $result = []; - $keys = array_flip($idList); + $keys = \array_flip($idList); foreach ($screens as $screen) { $metaModelName = $keys[$screen['id']]; $result[$metaModelName] = $this->prepareInputScreen($metaModelName, $screen); @@ -96,21 +128,34 @@ public function fetchInputScreens($idList): array * @param string $modelName The MetaModel name. * @param array $screen The screen meta data. * - * @return array + * @return array{ + * meta: array, + * properties: array, + * conditions: list}>, + * groupSort: array, + * label: array, + * description: array, + * legends: array, + * } * * @throws \InvalidArgumentException When the MetaModel can not be retrieved. */ - private function prepareInputScreen($modelName, $screen): array + private function prepareInputScreen(string $modelName, array $screen): array { if (null === $metaModel = $this->factory->getMetaModel($modelName)) { throw new \InvalidArgumentException('Could not retrieve MetaModel ' . $modelName); } $caption = ['' => $metaModel->getName()]; - $description = ['' => $metaModel->getName()]; + $description = ['' => '']; $fallback = null; + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ if ($metaModel instanceof ITranslatedMetaModel) { $fallback = $metaModel->getMainLanguage(); } elseif ($metaModel->isTranslated(false)) { + /** @psalm-suppress DeprecatedMethod */ $fallback = $metaModel->getFallbackLanguage(); } foreach (StringUtil::deserialize($screen['backendcaption'], true) as $languageEntry) { @@ -152,28 +197,33 @@ private function buildConditionTree(array $conditions): array $bySetting = []; foreach ($conditions as $condition) { unset($converted); + $conditionId = $condition['id']; + $conditionPid = $condition['pid']; + $conditionSettingId = $condition['settingId']; // Check if already mapped, if so, we need to set the values. - if (array_key_exists($condition['id'], $conditionMap)) { - $converted = &$conditionMap[$condition['id']]; + if (\array_key_exists($conditionId, $conditionMap)) { + $converted = &$conditionMap[$conditionId]; foreach ($condition as $key => $value) { $converted[$key] = $value; } } else { - $converted = \array_slice($condition, 0); - $conditionMap[$condition['id']] = &$converted; + $converted = \array_slice($condition, 0); + $conditionMap[$conditionId] = &$converted; } // Is on root level - add to setting now. - if (empty($condition['pid'])) { - $bySetting[$condition['settingId']][] = &$converted; + if (empty($conditionPid)) { + /** @psalm-suppress UnsupportedReferenceUsage */ + $bySetting[$conditionSettingId][] = &$converted; continue; } // Is a child, check if parent already added. - if (!isset($conditionMap[$condition['pid']])) { - $temp = ['children' => []]; - $conditionMap[$condition['pid']] = &$temp; + if (!isset($conditionMap[$conditionPid])) { + $temp = ['children' => []]; + $conditionMap[$conditionPid] = &$temp; } // Add child to parent now. - $conditionMap[$condition['pid']]['children'][] = &$converted; + /** @psalm-suppress UnsupportedReferenceUsage */ + $conditionMap[$conditionPid]['children'][] = &$converted; } return $bySetting; @@ -186,32 +236,36 @@ private function buildConditionTree(array $conditions): array * @param IMetaModel $metaModel The MetaModel to fetch properties for. * * @return array + * @throws Exception */ - private function fetchPropertiesFor($inputScreenId, IMetaModel $metaModel): array + private function fetchPropertiesFor(string $inputScreenId, IMetaModel $metaModel): array { $builder = $this->connection->createQueryBuilder(); - return array_map(function ($column) use ($inputScreenId, $metaModel) { - if ('attribute' !== $column['dcatype']) { - return $column; - } - if (!($attribute = $metaModel->getAttributeById($column['attr_id']))) { - // @codingStandardsIgnoreStart - @trigger_error( - 'Unknown attribute "' . $column['attr_id'] . '" in input screen "' . $inputScreenId . '"', - E_USER_WARNING - ); - // @codingStandardsIgnoreEnd - return $column; - } - $column = array_merge( - $column, - $attribute->getFieldDefinition($column), - ['col_name' => $attribute->getColName()] - ); + return \array_map( + static function ($column) use ($inputScreenId, $metaModel) { + if ('attribute' !== $column['dcatype']) { + return $column; + } + if (!($attribute = $metaModel->getAttributeById((int)$column['attr_id']))) { + // @codingStandardsIgnoreStart + @trigger_error( + 'Unknown attribute "' . $column['attr_id'] . '" in input screen "' . $inputScreenId . '"', + E_USER_WARNING + ); + + // @codingStandardsIgnoreEnd + return $column; + } + + $column = \array_merge( + $column, + $attribute->getFieldDefinition($column), + ['col_name' => $attribute->getColName()] + ); - return $column; - }, + return $column; + }, $builder ->select('t.*') ->from('tl_metamodel_dcasetting', 't') @@ -220,8 +274,9 @@ private function fetchPropertiesFor($inputScreenId, IMetaModel $metaModel): arra ->setParameter('pid', $inputScreenId) ->setParameter('published', 1) ->orderBy('t.sorting') - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC)); + ->executeQuery() + ->fetchAllAssociative() + ); } /** @@ -229,9 +284,9 @@ private function fetchPropertiesFor($inputScreenId, IMetaModel $metaModel): arra * * @param string $inputScreenId The input screen to obtain conditions for. * - * @return array + * @return list}> */ - private function fetchConditions($inputScreenId): array + private function fetchConditions(string $inputScreenId): array { $builder = $this->connection->createQueryBuilder(); @@ -246,8 +301,8 @@ private function fetchConditions($inputScreenId): array ->setParameter('screenId', $inputScreenId) ->orderBy('cond.pid') ->addOrderBy('cond.sorting') - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); } /** @@ -257,47 +312,52 @@ private function fetchConditions($inputScreenId): array * @param IMetaModel $metaModel The MetaModel to fetch properties for. * * @return array + * + * @throws Exception */ - private function fetchGroupSort($inputScreenId, IMetaModel $metaModel): array + private function fetchGroupSort(string $inputScreenId, IMetaModel $metaModel): array { $builder = $this->connection->createQueryBuilder(); - return array_map(function ($information) use ($inputScreenId, $metaModel) { - $information['isdefault'] = (bool) $information['isdefault']; - $information['ismanualsort'] = (bool) $information['ismanualsort']; - $information['rendergrouplen'] = (int) $information['rendergrouplen']; - if ($information['ismanualsort']) { - $information['rendergrouptype'] = 'none'; - } + return \array_map( + static function ($information) use ($inputScreenId, $metaModel) { + $information['isdefault'] = (bool)$information['isdefault']; + $information['ismanualsort'] = (bool)$information['ismanualsort']; + $information['rendergrouplen'] = (int)$information['rendergrouplen']; + if ($information['ismanualsort']) { + $information['rendergrouptype'] = 'none'; + } - if (!empty($information['rendersortattr'])) { - if (!($attribute = $metaModel->getAttributeById($information['rendersortattr']))) { - // @codingStandardsIgnoreStart - @trigger_error( - sprintf( - 'Unknown attribute "%1$s" in group sorting "%2$s.%3$s"', - $information['rendersortattr'], - $inputScreenId, - $information['id'] - ), - E_USER_WARNING - ); - // @codingStandardsIgnoreEnd - return $information; + if (!empty($information['rendersortattr'])) { + if (!($attribute = $metaModel->getAttributeById((int)$information['rendersortattr']))) { + // @codingStandardsIgnoreStart + @trigger_error( + \sprintf( + 'Unknown attribute "%1$s" in group sorting "%2$s.%3$s"', + $information['rendersortattr'], + $inputScreenId, + $information['id'] + ), + E_USER_WARNING + ); + + // @codingStandardsIgnoreEnd + return $information; + } + $information['col_name'] = $attribute->getColName(); } - $information['col_name'] = $attribute->getColName(); - } - return $information; - }, + return $information; + }, $builder ->select('t.*') ->from('tl_metamodel_dca_sortgroup', 't') ->where('t.pid=:screenId') ->setParameter('screenId', $inputScreenId) ->orderBy('t.sorting') - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC)); + ->executeQuery() + ->fetchAllAssociative() + ); } /** @@ -305,7 +365,6 @@ private function fetchGroupSort($inputScreenId, IMetaModel $metaModel): array * * @param array $properties The property and legend information. * @param IMetaModel $metaModel The MetaModel to fetch properties for. - * * @param array $conditions The conditions for the entries. * * @return array @@ -313,6 +372,10 @@ private function fetchGroupSort($inputScreenId, IMetaModel $metaModel): array private function convertLegends(array $properties, IMetaModel $metaModel, array $conditions): array { $result = []; + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ $trans = (($metaModel instanceof ITranslatedMetaModel) || $metaModel->isTranslated(false)); $label = $this->buildLabel($trans, $metaModel); $legend = [ @@ -321,8 +384,8 @@ private function convertLegends(array $properties, IMetaModel $metaModel, array 'properties' => [] ]; - $condition = function ($property) use ($conditions) { - if (!isset($conditions[$property['id']])) { + $condition = function (array $property) use ($conditions): ?array { + if (!isset($conditions[($property['id'])])) { return null; } @@ -333,9 +396,14 @@ private function convertLegends(array $properties, IMetaModel $metaModel, array }; $fallbackLanguage = null; + /** + * @psalm-suppress DeprecatedMethod + * @psalm-suppress TooManyArguments + */ if ($metaModel instanceof ITranslatedMetaModel) { $fallbackLanguage = $metaModel->getMainLanguage(); } elseif ($metaModel->isTranslated(false)) { + /** @psalm-suppress DeprecatedMethod */ $fallbackLanguage = $metaModel->getFallbackLanguage(); } foreach ($properties as $property) { @@ -372,9 +440,11 @@ private function buildLabel(bool $isTranslated, IMetaModel $metaModel): array { if ($isTranslated) { $label = []; - foreach (($metaModel instanceof ITranslatedMetaModel) + /** @psalm-suppress DeprecatedMethod */ + foreach ( + ($metaModel instanceof ITranslatedMetaModel) ? $metaModel->getLanguages() - : $metaModel->getAvailableLanguages() as $availableLanguage + : ($metaModel->getAvailableLanguages() ?? []) as $availableLanguage ) { $label[$availableLanguage] = $metaModel->getName(); } diff --git a/src/ViewCombination/ViewCombination.php b/src/ViewCombination/ViewCombination.php index 3153ec5c9..9d43cd3af 100644 --- a/src/ViewCombination/ViewCombination.php +++ b/src/ViewCombination/ViewCombination.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2019 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,8 @@ * @author Christian Schiffler * @author Sven Baumann * @author Richard Henkenjohann - * @copyright 2012-2019 The MetaModels team. + * @author Ingolf Steinhardt + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -36,28 +37,28 @@ class ViewCombination * * @var Cache */ - private $cache; + private Cache $cache; /** * The token storage. * * @var TokenStorageInterface */ - private $tokenStorage; + private TokenStorageInterface $tokenStorage; /** * The combination builder. * * @var ViewCombinationBuilder */ - private $builder; + private ViewCombinationBuilder $builder; /** * The input screen information builder. * * @var InputScreenInformationBuilder */ - private $inputScreens; + private InputScreenInformationBuilder $inputScreens; /** * Create a new instance. @@ -93,11 +94,11 @@ public function getCombinations() $mode = 'be'; // Try to get the group(s) // there might be a NULL in there as BE admins have no groups and - // user might have one but it is not mandatory. + // user might have one, but it is not mandatory. // I would prefer a default group for both, fe and be groups. $groups = $user->groups; // Special case in combinations, admins have the implicit group id -1. - if ($user->admin) { + if ((bool) $user->admin) { $groups[] = -1; } @@ -117,9 +118,9 @@ public function getCombinations() $groups = [-1]; } - $groups = array_filter($groups); + $groups = \array_filter($groups); - if ($this->cache->contains($cacheKey = 'combinations_' . $mode . '_' . implode(',', $groups))) { + if ($this->cache->contains($cacheKey = 'combinations_' . $mode . '_' . \implode(',', $groups))) { return $this->cache->fetch($cacheKey); } @@ -140,21 +141,18 @@ public function getCombinations() public function getCombination($tableName) { $combinations = $this->getCombinations(); - if (isset($combinations['byName'][$tableName])) { - return $combinations['byName'][$tableName]; - } - return null; + return $combinations['byName'][$tableName] ?? null; } /** - * Obtain stand alone input screens. + * Obtain stand-alone input screens. * * @return array */ public function getStandalone() { - $inputScreens = array_filter($this->getInputScreens(), function ($inputScreen) { + $inputScreens = \array_filter($this->getInputScreens(), static function ($inputScreen) { return $inputScreen['meta']['rendertype'] === 'standalone'; }); @@ -168,7 +166,7 @@ public function getStandalone() */ public function getParented() { - $inputScreens = array_filter($this->getInputScreens(), function ($inputScreen) { + $inputScreens = \array_filter($this->getInputScreens(), static function ($inputScreen) { return $inputScreen['meta']['rendertype'] === 'ctable'; }); @@ -184,7 +182,7 @@ public function getParented() */ public function getChildrenOf($parentTable) { - $inputScreens = array_filter($this->getInputScreens(), function ($inputScreen) use ($parentTable) { + $inputScreens = \array_filter($this->getInputScreens(), static function ($inputScreen) use ($parentTable) { return ($inputScreen['meta']['rendertype'] === 'ctable') && ($inputScreen['meta']['ptable'] === $parentTable); }); @@ -202,11 +200,8 @@ public function getChildrenOf($parentTable) public function getScreen($tableName) { $inputScreens = $this->getInputScreens(); - if (isset($inputScreens[$tableName])) { - return $inputScreens[$tableName]; - } - return null; + return $inputScreens[$tableName] ?? null; } /** @@ -221,11 +216,11 @@ private function getInputScreens() return []; } - $screenIds = array_map(function ($combination) { + $screenIds = \array_map(static function (array $combination): mixed { return $combination['dca_id']; }, $combinations['byName']); - if ($this->cache->contains($cacheKey = 'screens_' . implode(',', $screenIds))) { + if ($this->cache->contains($cacheKey = 'screens_' . \implode(',', $screenIds))) { return $this->cache->fetch($cacheKey); } diff --git a/src/ViewCombination/ViewCombinationBuilder.php b/src/ViewCombination/ViewCombinationBuilder.php index 9154bf45b..e55c60852 100644 --- a/src/ViewCombination/ViewCombinationBuilder.php +++ b/src/ViewCombination/ViewCombinationBuilder.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,13 +14,14 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\ViewCombination; +use Doctrine\DBAL\ArrayParameterType; use Doctrine\DBAL\Connection; use MetaModels\IFactory; @@ -34,14 +35,14 @@ class ViewCombinationBuilder * * @var Connection */ - private $connection; + private Connection $connection; /** * The MetaModels factory. * * @var IFactory */ - private $factory; + private IFactory $factory; /** * Create a new instance. @@ -67,7 +68,7 @@ public function __construct(Connection $connection, IFactory $factory) */ public function getCombinationsForUser($userGroups, $userType) { - $userType = strtolower($userType); + $userType = \strtolower($userType); if ('fe' !== $userType && 'be' !== $userType) { throw new \InvalidArgumentException('Unknown user type: ' . $userType); } @@ -78,12 +79,12 @@ public function getCombinationsForUser($userGroups, $userType) /** * Retrieve the palette combinations from the database. * - * @param string $userGroups The user groups of the user to fetch information for. - * @param string $userType The user type. + * @param string[] $userGroups The user groups of the user to fetch information for. + * @param string $userType The user type. * * @return null|array */ - private function getCombinationsFromDatabase($userGroups, $userType) + private function getCombinationsFromDatabase(array $userGroups, string $userType): ?array { if (empty($userGroups)) { return null; @@ -94,15 +95,15 @@ private function getCombinationsFromDatabase($userGroups, $userType) ->createQueryBuilder(); $combinations = $builder - ->select('*') + ->select('t.*') ->from('tl_metamodel_dca_combine', 't') ->where($builder->expr()->in('t.' . $userType . '_group', ':groupList')) - ->setParameter('groupList', $userGroups, Connection::PARAM_STR_ARRAY) + ->setParameter('groupList', $userGroups, ArrayParameterType::STRING) ->orWhere('t.' . $userType . '_group=0') ->orderBy('t.pid') ->addOrderBy('t.sorting') - ->execute() - ->fetchAll(\PDO::FETCH_ASSOC); + ->executeQuery() + ->fetchAllAssociative(); $result = [ 'byName' => [], diff --git a/src/Widgets/FileSelectorWidget.php b/src/Widgets/FileSelectorWidget.php index d16f75466..2db608e63 100644 --- a/src/Widgets/FileSelectorWidget.php +++ b/src/Widgets/FileSelectorWidget.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. @@ -14,7 +14,8 @@ * @author David Maack * @author Christian Schiffler * @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 */ @@ -26,6 +27,9 @@ /** * Provide methods to handle input field "file tree". + * + * @psalm-suppress PropertyNotSetInConstructor + * @psalm-suppress DeprecatedClass */ class FileSelectorWidget extends FileSelector { @@ -41,38 +45,42 @@ class FileSelectorWidget extends FileSelector */ public function __construct($arrAttributes = null) { + /** @psalm-suppress DeprecatedClass */ parent::__construct($arrAttributes); if (!$this->strField) { - $this->strField = $arrAttributes['name']; + $this->strField = $arrAttributes['name'] ?? ''; } // Strip the leading "ctrl_" from the field name if present. - if (substr($this->strField, 0, 5) === 'ctrl_') { - $chunks = explode('_', $this->strField); - array_shift($chunks); - $this->strField = implode('_', $chunks); + if (\str_starts_with($this->strField ?? '', 'ctrl_')) { + $chunks = \explode('_', $this->strField ?? ''); + \array_shift($chunks); + $this->strField = \implode('_', $chunks); } } /** * Generate a particular sub part of the file tree and return it as HTML string. * - * @param string $folder The folder name. - * - * @param string $strField The property name. - * - * @param int $level The level where the given folder shall be rendered within. - * - * @param bool $mount Flag determining if the passed folder shall be handled as root level - * (optional, default: no). + * @param string $strFolder The folder name. + * @param string $strField The property name. + * @param int $level The level where the given folder shall be rendered within. + * @param bool $mount Flag determining if the passed folder shall be handled as root level + * (optional, default: no). * * @return string * * @SuppressWarnings(PHPMD.UnusedFormalParameter) + * + * @psalm-suppress ImplementedParamTypeMismatch */ - public function generateAjax($folder, $strField, $level, $mount = false) + public function generateAjax($strFolder, $strField, $level, $mount = false) { - return parent::generateAjax($folder, $this->strField, $level, $mount); + /** + * @psalm-suppress InvalidArgument + * @psalm-suppress DeprecatedClass + */ + return parent::generateAjax($strFolder, $this->strField, $level, $mount); } } diff --git a/src/Widgets/MultiTextWidget.php b/src/Widgets/MultiTextWidget.php index 5b5996d28..e54bfc3d8 100644 --- a/src/Widgets/MultiTextWidget.php +++ b/src/Widgets/MultiTextWidget.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2023 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2023 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +26,8 @@ /** * Form field with more than 1 input, based on form field by Leo Feyer. + * + * @psalm-suppress PropertyNotSetInConstructor */ class MultiTextWidget extends Widget { @@ -47,7 +49,6 @@ class MultiTextWidget extends Widget * Add specific attributes. * * @param string $strKey Name of the key to set. - * * @param mixed $varValue The value to use. * * @return void @@ -89,8 +90,8 @@ public function __set($strKey, $varValue) */ protected function validator($varInput) { - if (is_array($varInput)) { - $value = array(); + if (\is_array($varInput)) { + $value = []; foreach ($varInput as $key => $input) { $value[$key] = parent::validator($input); } @@ -110,16 +111,17 @@ protected function validator($varInput) public function generate() { $return = ''; + /** @psalm-suppress UndefinedThisPropertyFetch */ for ($i = 0; $i < $this->size; $i++) { - $return .= sprintf( + $return .= \sprintf( 'strName, $this->strId, $i, '', - (strlen($this->strClass) ? ' ' . $this->strClass : ''), - StringUtil::specialchars($this->varValue[$i]), + (\strlen($this->strClass) ? ' ' . $this->strClass : ''), + StringUtil::specialchars($this->varValue[$i] ?? ''), $this->getAttributes(), $this->strTagEnding ); diff --git a/src/Widgets/SubDcaWidget.php b/src/Widgets/SubDcaWidget.php index 2b68e02b6..8e1303991 100644 --- a/src/Widgets/SubDcaWidget.php +++ b/src/Widgets/SubDcaWidget.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -17,13 +17,14 @@ * @author Sven Baumann * @author Cliff Parnitzky * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ namespace MetaModels\Widgets; +use Contao\DataContainer; use Contao\Date; use Contao\StringUtil; use Contao\System; @@ -33,9 +34,14 @@ use ContaoCommunityAlliance\Contao\Bindings\Events\Widget\GetAttributesFromDcaEvent; use Exception; use Symfony\Component\EventDispatcher\EventDispatcherInterface; +use Symfony\Component\HttpFoundation\Request; /** * Provide methods to handle multiple widgets in one. + * + * @SuppressWarnings(PHPMD.ExcessiveClassComplexity) + * + * @psalm-suppress PropertyNotSetInConstructor */ class SubDcaWidget extends Widget { @@ -58,28 +64,28 @@ class SubDcaWidget extends Widget * * @var array */ - protected $arrOptions = array(); + protected $arrOptions = []; /** * SubFields. * * @var array */ - protected $arrSubFields = array(); + protected $arrSubFields = []; /** * Flag fields to be applied to each subfield. * * @var array */ - protected $arrFlagFields = array(); + protected $arrFlagFields = []; /** * The prepared widgets. * * @var array */ - protected $arrWidgets = array(); + protected $arrWidgets = []; /** * Initialize the object. @@ -89,14 +95,17 @@ class SubDcaWidget extends Widget public function __construct($attributes = false) { parent::__construct(); - $this->addAttributes($attributes); - // Input field callback. - if (isset($attributes['getsubfields_callback']) && is_array($attributes['getsubfields_callback'])) { - $arrCallback = $this->$attributes['getsubfields_callback']; - if (!is_object($arrCallback[0])) { - $this->import($arrCallback[0]); + if (\is_array($attributes)) { + $this->addAttributes($attributes); + + // Input field callback. + if (isset($attributes['getsubfields_callback']) && is_array($attributes['getsubfields_callback'])) { + $arrCallback = $this->$attributes['getsubfields_callback']; + if (!is_object($arrCallback[0])) { + $this->import($arrCallback[0]); + } + $this->arrSubFields = $this->{$arrCallback[0]}->{$arrCallback[1]}($this, $attributes); } - $this->arrSubFields = $this->{$arrCallback[0]}->{$arrCallback[1]}($this, $attributes); } } @@ -107,7 +116,6 @@ public function __construct($attributes = false) * widget does understand: 'options', 'subfields' and 'flagfields'. * * @param string $strKey The key of the attribute to set. - * * @param mixed $varValue The value to use. * * @return void @@ -145,14 +153,16 @@ public function __set($strKey, $varValue) */ public function getEventDispatcher() { - return System::getContainer()->get('event_dispatcher'); + $dispatcher = System::getContainer()->get('event_dispatcher'); + assert($dispatcher instanceof EventDispatcherInterface); + + return $dispatcher; } /** - * Generate an help wizard if needed. + * Generate a help wizard if needed. * * @param string $key The widget name. - * * @param array $field The field DCA - might get changed within this routine. * * @return string @@ -174,14 +184,15 @@ protected function getHelpWizard($key, $field) ); $this->getEventDispatcher()->dispatch($event, ContaoEvents::IMAGE_GET_HTML); - return sprintf( - ' %s', + /** @psalm-suppress UndefinedConstant */ + return \sprintf( + ' %s', TL_PATH . 'contao/', $this->strTable, $this->strName, $key, StringUtil::specialchars($GLOBALS['TL_LANG']['MSC']['helpWizard']), - $event->getHtml() + $event->getHtml() ?? '' ); } @@ -189,9 +200,7 @@ protected function getHelpWizard($key, $field) * Make fields mandatory if necessary. * * @param array $field The field DCA. - * * @param string $row The setting name. - * * @param string $key The widget name. * * @return array @@ -204,14 +213,12 @@ protected function makeMandatory($field, $row, $key) return $field; } - if (is_array($this->varValue[$row][$key])) { + if (\is_array($this->varValue[$row][$key])) { if (empty($this->varValue[$row][$key])) { $field['eval']['required'] = true; } - } else { - if (!strlen($this->varValue[$row][$key])) { - $field['eval']['required'] = true; - } + } elseif ($this->varValue[$row][$key] === '') { + $field['eval']['required'] = true; } return $field; @@ -229,20 +236,25 @@ protected function makeMandatory($field, $row, $key) */ protected function getWidgetClass($field) { - $className = $GLOBALS[(TL_MODE == 'BE' ? 'BE_FFL' : 'TL_FFL')][$field['inputType']]; + $isBackend = (bool) System::getContainer() + ->get('contao.routing.scope_matcher') + ?->isBackendRequest( + System::getContainer()->get('request_stack')?->getCurrentRequest() ?? Request::create('') + ); + /** @var class-string|null $strClass */ + $className = $GLOBALS[($isBackend ? 'BE_FFL' : 'TL_FFL')][$field['inputType']]; - if (($className !== '') && class_exists($className)) { + if (($className !== '') && \class_exists($className)) { return $className; } - return null; + return ''; } /** * Handle the onload_callback. * * @param array $field The field information. - * * @param mixed $value The value. * * @return mixed @@ -250,7 +262,7 @@ protected function getWidgetClass($field) protected function handleLoadCallback($field, $value) { // Load callback. - if (isset($field['load_callback']) && is_array($field['load_callback'])) { + if (isset($field['load_callback']) && \is_array($field['load_callback'])) { foreach ($field['load_callback'] as $callback) { $this->import($callback[0]); $value = $this->{$callback[0]}->{$callback[1]}($value, $this); @@ -266,11 +278,8 @@ protected function handleLoadCallback($field, $value) * Based on DataContainer::row() from Contao 2.10.1. * * @param array $arrField The field DCA - might get changed within this routine. - * * @param string $strRow The setting name. - * * @param string $strKey The widget name. - * * @param mixed $varValue The widget value. * * @return Widget|null The widget on success, null otherwise. @@ -280,14 +289,15 @@ protected function initializeWidget(&$arrField, $strRow, $strKey, $varValue) $xlabel = $this->getHelpWizard($strKey, $arrField); // Input field callback. - if (isset($arrField['input_field_callback']) && is_array($arrField['input_field_callback'])) { - if (!is_object($this->$arrField['input_field_callback'][0])) { + if (isset($arrField['input_field_callback']) && \is_array($arrField['input_field_callback'])) { + if (!\is_object($this->$arrField['input_field_callback'][0])) { $this->import($arrField['input_field_callback'][0]); } return $this->{$arrField['input_field_callback'][0]}->$arrField['input_field_callback'][1]($this, $xlabel); } + /** @var class-string|null $strClass */ $strClass = $this->getWidgetClass($arrField); if (empty($strClass)) { @@ -299,7 +309,7 @@ protected function initializeWidget(&$arrField, $strRow, $strKey, $varValue) $arrField['name'] = $this->strName . '[' . $strRow . '][' . $strKey . ']'; $arrField['id'] = $this->strId . '_' . $strRow . '_' . $strKey; - $arrField['value'] = ($varValue !== '') ? $varValue : $arrField['default']; + $arrField['value'] = ($varValue !== '') ? $varValue : ($arrField['default'] ?? ''); $arrField['eval']['tableless'] = true; $event = new GetAttributesFromDcaEvent( @@ -308,15 +318,19 @@ protected function initializeWidget(&$arrField, $strRow, $strKey, $varValue) $arrField['value'], '', $this->strTable, - $this->objDca + ((\is_a($this->objDca, DataContainer::class)) ? $this->objDca : null) ); $this->getEventDispatcher()->dispatch($event, ContaoEvents::WIDGET_GET_ATTRIBUTES_FROM_DCA); + /** @psalm-suppress UnsafeInstantiation */ $objWidget = new $strClass($event->getResult()); + if (!($objWidget instanceof Widget)) { + return null; + } - $objWidget->strId = $arrField['id']; - $objWidget->storeValues = true; + $objWidget->strId = (int) $arrField['id']; + $objWidget->storeValues = 1; // The type is int in the widget. $objWidget->xlabel = $xlabel; return $objWidget; @@ -333,15 +347,15 @@ protected function prepareWidgets() return; } - $arrWidgets = array(); + $arrWidgets = []; foreach ($this->arrSubFields as $strFieldName => &$arrSubField) { - $varValue = $this->value[$strFieldName]; - $arrRow = array(); + $varValue = $this->value[$strFieldName] ?? []; + $arrRow = []; $objWidget = $this->initializeWidget( $arrSubField, $strFieldName, 'value', - $varValue['value'] + $varValue['value'] ?? null ); if (!$objWidget) { @@ -353,7 +367,7 @@ protected function prepareWidgets() $arrFlagField, $strFieldName, $strFlag, - $varValue[$strFlag] + $varValue[$strFlag] ?? null ); if ($objWidget) { @@ -369,9 +383,7 @@ protected function prepareWidgets() * Handle the onsave_callback for a widget. * * @param array $field The field DCA. - * * @param Widget $widget The widget to validate. - * * @param mixed $value The value. * * @return mixed @@ -380,7 +392,7 @@ protected function handleSaveCallback($field, $widget, $value) { $newValue = $value; - if (isset($field['save_callback']) && is_array($field['save_callback'])) { + if (isset($field['save_callback']) && \is_array($field['save_callback'])) { foreach ($field['save_callback'] as $callback) { $this->import($callback[0]); @@ -404,11 +416,8 @@ protected function handleSaveCallback($field, $widget, $value) * Based on DataContainer::row() from Contao 2.10.1 * * @param array $arrField The field DCA. - * * @param string $strRow The setting name. - * * @param string $strKey The widget name. - * * @param mixed $varInput The overall input value. * * @return bool @@ -418,9 +427,9 @@ protected function handleSaveCallback($field, $widget, $value) */ protected function validateWidget(&$arrField, $strRow, $strKey, &$varInput) { - $varValue = $varInput[$strRow][$strKey]; + $varValue = $varInput[$strRow][$strKey] ?? ''; $objWidget = $this->initializeWidget($arrField, $strRow, $strKey, $varValue); - if (!is_object($objWidget)) { + if (!\is_object($objWidget)) { return false; } @@ -434,7 +443,7 @@ protected function validateWidget(&$arrField, $strRow, $strKey, &$varInput) $varValue = $objWidget->value; // Convert date formats into timestamps (check the eval setting first -> #3063). - $rgxp = $arrField['eval']['rgxp']; + $rgxp = $arrField['eval']['rgxp'] ?? null; if (($rgxp == 'date' || $rgxp == 'time' || $rgxp == 'datim') && $varValue != '') { $objDate = new Date($varValue, $GLOBALS['TL_CONFIG'][$rgxp . 'Format']); $varValue = $objDate->tstamp; @@ -497,10 +506,11 @@ protected function validator($varInput) */ protected function getHelpForWidget($widget) { - if ($GLOBALS['TL_CONFIG']['showHelp'] && $widget->description) { - return sprintf( + if (!empty($GLOBALS['TL_CONFIG']['showHelp']) && !empty($widget->description)) { + /** @psalm-suppress UndefinedMagicPropertyFetch */ + return \sprintf( '

    %s

    ', - $widget->tl_class, + (string) $widget->tl_class, $widget->description ); } @@ -511,21 +521,23 @@ protected function getHelpForWidget($widget) /** * Build the options for a widget. * - * @return array. + * @return array */ protected function buildOptions() { - $options = array(); + $options = []; foreach ($this->arrWidgets as $widgetRow) { - $columns = array(); - foreach ($widgetRow as $widget) { + $columns = []; + foreach ((array) $widgetRow as $widget) { /** @var Widget $widget */ - $valign = ($widget->valign != '' ? ' valign="' . $widget->valign . '"' : ''); - $class = ($widget->tl_class != '' ? ' class="' . $widget->tl_class . '"' : ''); + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $valign = ($widget->valign != '' ? ' valign="' . ($widget->valign ?? '') . '"' : ''); + /** @psalm-suppress UndefinedMagicPropertyFetch */ + $class = ($widget->tl_class != '' ? ' class="' . ($widget->tl_class ?? '') . '"' : ''); $style = ($widget->style != '' ? ' style="' . $widget->style . '"' : ''); $help = $this->getHelpForWidget($widget); - $columns[] = sprintf( + $columns[] = \sprintf( '%4$s%5$s', $valign, $class, @@ -534,7 +546,7 @@ protected function buildOptions() $help ); } - $options[] = implode('', $columns); + $options[] = \implode('', $columns); } return $options; @@ -557,25 +569,27 @@ public function generate() $arrOptions = $this->buildOptions(); // Add a "no entries found" message if there are no sub widgets. - if (!count($arrOptions)) { - $arrOptions[] = '

    '.$GLOBALS['TL_LANG']['MSC']['noResult'].'

    '; + if (!\count($arrOptions)) { + $arrOptions[] = '

    ' + . (string) ($GLOBALS['TL_LANG']['MSC']['noResult'] ?? '') + . '

    '; } $strHead = ''; - $strBody = sprintf('%s', implode("\n", $arrOptions)); + $strBody = \sprintf('%s', \implode("\n", $arrOptions)); - $strOutput = sprintf( - '%s%s
    ', + $strOutput = \sprintf( + '%s%s', (($this->style) ? ('style="' . $this->style . '"') : ('')), $this->strId, $strHead, $strBody ); - return sprintf( + return \sprintf( '
    %s
    ', $this->strName, - (strlen($this->strClass) ? ' ' . $this->strClass : ''), + (\strlen($this->strClass) ? ' ' . $this->strClass : ''), $strOutput ); } diff --git a/src/Widgets/TagsWidget.php b/src/Widgets/TagsWidget.php index 7f830ec1c..86f75ee52 100644 --- a/src/Widgets/TagsWidget.php +++ b/src/Widgets/TagsWidget.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2021 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -15,7 +15,7 @@ * @author Stefan Heimes * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2021 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -26,6 +26,12 @@ /** * Form field "tags", based on form field by Leo Feyer. + * + * @psalm-type TTagOption=array{value: string, label: string} + * + * @property list|null $options + * + * @psalm-suppress PropertyNotSetInConstructor */ class TagsWidget extends Widget { @@ -36,7 +42,6 @@ class TagsWidget extends Widget */ protected $strTemplate = 'form_tags'; - /** * {@inheritDoc} */ @@ -74,11 +79,11 @@ public function __set($strKey, $varValue) */ protected function validator($varInput) { - if (is_array($varInput)) { + if (\is_array($varInput)) { return parent::validator($varInput); } - return parent::validator(trim($varInput)); + return parent::validator(\trim((string) ($varInput ?? ''))); } /** @@ -88,51 +93,50 @@ protected function validator($varInput) * * @return string */ - protected function getClassForOption($index) + protected function getClassForOption(int $index): string { // If true we need another offset. $intSub = ($this->arrConfiguration['includeBlankOption'] ? 1 : 0); - $intSub += ($this->arrConfiguration['showSelectAll'] - || null === $this->arrConfiguration['showSelectAll'] ? 1 : 0); + $intSub += ((isset($this->arrConfiguration['showSelectAll']) + && null !== $this->arrConfiguration['showSelectAll']) ? 0 : 1); $strClass = $this->strName; - if ($index == 0) { + if ($index === 0) { $strClass .= ' first'; - } elseif ($index === (count($this->options) - 1 + $intSub)) { + } elseif ($index === (\count($this->options ?? []) - 1 + $intSub)) { $strClass .= ' last'; } - if (($index % 2) == 1) { + if (($index % 2) === 1) { $strClass .= ' even'; } else { $strClass .= ' odd'; } - return ((strlen($this->strClass)) ? $this->strClass . ' ' : '') . $strClass; + return ((\strlen($this->strClass)) ? $this->strClass . ' ' : '') . $strClass; } /** * Generate a single checkbox. * - * @param array $val The value array (needs keys "value" and "label"). - * - * @param int $index The sequence number of this option (used for even/odd determination). + * @param TTagOption $val The value array (needs keys "value" and "label"). + * @param int $index The sequence number of this option (used for even/odd determination). * * @return string */ - protected function generateOption($val, $index) + protected function generateOption(array $val, int $index): string { $checked = ''; - if (is_array($this->varValue) && in_array($val['value'], $this->varValue)) { + if (\is_array($this->varValue) && \in_array($val['value'], $this->varValue)) { $checked = ' checked="checked"'; } - return sprintf( + return \sprintf( '' . '%7$s', // @codingStandardsIgnoreStart - Keep the comments. - $this->getClassForOption($index), // 1 + $this->getClassForOption($index), // 1 $index, // 2 $this->strName . '_' . $index, // 3 $val['value'], // 4 @@ -140,7 +144,7 @@ protected function generateOption($val, $index) $this->getAttributes() . $this->strTagEnding, // 6 $val['label'], // 7 $this->strName // 8 - // @codingStandardsIgnoreEnd + // @codingStandardsIgnoreEnd ); } @@ -150,9 +154,9 @@ protected function generateOption($val, $index) * @SuppressWarnings(PHPMD.Superglobals) * @SuppressWarnings(PHPMD.CamelCaseVariableName) */ - public function generate() + public function generate(): string { - $return = sprintf( + $return = \sprintf( '
    ', $this->strName @@ -160,21 +164,27 @@ public function generate() $count = 0; - if ($this->options && is_array($this->options)) { + /** @psalm-suppress MixedAssignment */ + $langBase = $GLOBALS['TL_LANG']['metamodels_frontendfilter'] ?? []; + assert(\is_array($langBase)); + if (\is_array($this->options) && [] !== $this->options) { // Show not filter option. - if ($this->arrConfiguration['includeBlankOption']) { + if ((bool) $this->arrConfiguration['includeBlankOption']) { $return .= $this->generateOption( - ['value' => '--none--', 'label' => $this->arrConfiguration['blankOptionLabel']], + [ + 'value' => '--none--', + 'label' => (string) ($this->arrConfiguration['blankOptionLabel'] ?? '') + ], $count++ ); } // Show select all checkbox - check null as BC-Layer. - if ($this->arrConfiguration['showSelectAll'] || null === $this->arrConfiguration['showSelectAll']) { + if ((bool) $this->arrConfiguration['showSelectAll']) { $return .= $this->generateOption( [ 'value' => '--all--', - 'label' => $GLOBALS['TL_LANG']['metamodels_frontendfilter']['select_all'] + 'label' => (string) ($langBase['select_all'] ?? '') ], $count++ ); @@ -184,7 +194,7 @@ public function generate() $return .= $this->generateOption($val, $count++); } } else { - $return .= '' . $GLOBALS['TL_LANG']['metamodels_frontendfilter']['no_combinations'] . ''; + $return .= '' . (string) ($langBase['no_combinations'] ?? '') . ''; } $return .= '
    '; diff --git a/tests/Attribute/AttributeFactoryTest.php b/tests/Attribute/AttributeFactoryTest.php index c5ce38b2c..c8823ddb7 100644 --- a/tests/Attribute/AttributeFactoryTest.php +++ b/tests/Attribute/AttributeFactoryTest.php @@ -165,6 +165,8 @@ public function testAttributeTypeMatchesFlags() * Test that the method attributeTypeMatchesFlags() works correctly. * * @return void + * + * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function testGetTypeNames() { @@ -277,7 +279,7 @@ public function testGetTypeNames() public function testGetTypeIcon() { $factory = new AttributeFactory($this->getMockForAbstractClass(EventDispatcherInterface::class)); - $typeFactory = $this->mockAttributeFactory('test', true, false, false, new \stdClass, 'icon.png'); + $typeFactory = $this->mockAttributeFactory('test', true, false, false, new \stdClass(), 'icon.png'); $factory->addTypeFactory($typeFactory); self::assertEquals($typeFactory->getTypeIcon(), $factory->getIconForType('test')); diff --git a/tests/Attribute/BaseTest.php b/tests/Attribute/BaseTest.php index 0d1ce491b..382036b2b 100644 --- a/tests/Attribute/BaseTest.php +++ b/tests/Attribute/BaseTest.php @@ -169,6 +169,8 @@ public function testDoesNotAcceptArbitraryConfiguration() * Test that the attribute does not accept config keys not specified via getAttributeSettingNames(). * * @return void + * + * @SuppressWarnings(PHPMD.Superglobals) */ public function testGetFieldDefinition() { diff --git a/tests/CoreBundle/DependencyInjection/CompilerPass/CollectDoctrineSchemaGeneratorsPassTest.php b/tests/CoreBundle/DependencyInjection/CompilerPass/CollectDoctrineSchemaGeneratorsPassTest.php new file mode 100644 index 000000000..19abb02c9 --- /dev/null +++ b/tests/CoreBundle/DependencyInjection/CompilerPass/CollectDoctrineSchemaGeneratorsPassTest.php @@ -0,0 +1,82 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\CoreBundle\DependencyInjection\CompilerPass; + +use MetaModels\CoreBundle\DependencyInjection\CompilerPass\CollectDoctrineSchemaGeneratorsPass; +use MetaModels\Schema\Doctrine\DoctrineSchemaGenerator; +use MetaModels\Schema\SchemaGenerator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * This tests the schema generator collecting. + * + * @covers \MetaModels\CoreBundle\DependencyInjection\CompilerPass\CollectDoctrineSchemaGeneratorsPass + */ +class CollectDoctrineSchemaGeneratorsPassTest extends TestCase +{ + /** + * Test that all collectors are found. + * + * @return void + */ + public function testProcess(): void + { + $container = new ContainerBuilder(); + + $generator = $this + ->getMockBuilder(Definition::class) + ->setMethods(['getArgument', 'setArgument']) + ->getMock(); + $generator + ->expects($this->once()) + ->method('getArgument') + ->with(0) + ->willReturn([new Reference('previous-argument')]); + $generator + ->expects($this->once()) + ->method('setArgument') + ->willReturnCallback(function ($index, $children) { + $this->assertSame(0, $index); + $this->assertCount(3, $children); + $this->assertSame('previous-argument', (string) $children[0]); + $this->assertSame('child1', (string) $children[1]); + $this->assertSame('child2', (string) $children[2]); + }); + + $container->setDefinition(DoctrineSchemaGenerator::class, $generator); + + $child1 = new Definition(); + $child1->addTag(CollectDoctrineSchemaGeneratorsPass::TAG_NAME, ['priority' => 10]); + $child2 = new Definition(); + $child2->addTag(CollectDoctrineSchemaGeneratorsPass::TAG_NAME); + + $container->setDefinition('child2', $child2); + $container->setDefinition('child1', $child1); + + $pass = new CollectDoctrineSchemaGeneratorsPass(); + + $pass->process($container); + } +} diff --git a/tests/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaGeneratorsPassTest.php b/tests/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaGeneratorsPassTest.php new file mode 100644 index 000000000..a4ca2fbf2 --- /dev/null +++ b/tests/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaGeneratorsPassTest.php @@ -0,0 +1,81 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\CoreBundle\DependencyInjection\CompilerPass; + +use MetaModels\CoreBundle\DependencyInjection\CompilerPass\CollectSchemaGeneratorsPass; +use MetaModels\Schema\SchemaGenerator; +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * This tests the schema generator collecting. + * + * @covers \MetaModels\CoreBundle\DependencyInjection\CompilerPass\CollectSchemaGeneratorsPass + */ +class CollectSchemaGeneratorsPassTest extends TestCase +{ + /** + * Test that all collectors are found. + * + * @return void + */ + public function testProcess(): void + { + $container = new ContainerBuilder(); + + $generator = $this + ->getMockBuilder(Definition::class) + ->setMethods(['getArgument', 'setArgument']) + ->getMock(); + $generator + ->expects($this->once()) + ->method('getArgument') + ->with(0) + ->willReturn([new Reference('previous-argument')]); + $generator + ->expects($this->once()) + ->method('setArgument') + ->willReturnCallback(function ($index, $children) { + $this->assertSame(0, $index); + $this->assertCount(3, $children); + $this->assertSame('previous-argument', (string) $children[0]); + $this->assertSame('child1', (string) $children[1]); + $this->assertSame('child2', (string) $children[2]); + }); + + $container->setDefinition(SchemaGenerator::class, $generator); + + $child1 = new Definition(); + $child1->addTag(CollectSchemaGeneratorsPass::TAG_NAME, ['priority' => 10]); + $child2 = new Definition(); + $child2->addTag(CollectSchemaGeneratorsPass::TAG_NAME); + + $container->setDefinition('child2', $child2); + $container->setDefinition('child1', $child1); + + $pass = new CollectSchemaGeneratorsPass(); + + $pass->process($container); + } +} diff --git a/tests/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaManagersPassTest.php b/tests/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaManagersPassTest.php new file mode 100644 index 000000000..e24a99ca9 --- /dev/null +++ b/tests/CoreBundle/DependencyInjection/CompilerPass/CollectSchemaManagersPassTest.php @@ -0,0 +1,81 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\CoreBundle\DependencyInjection\CompilerPass; + +use MetaModels\CoreBundle\DependencyInjection\CompilerPass\CollectSchemaManagersPass; +use MetaModels\Schema\SchemaManager; +use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Definition; +use Symfony\Component\DependencyInjection\Reference; + +/** + * This tests the schema generator collecting. + * + * @covers \MetaModels\CoreBundle\DependencyInjection\CompilerPass\CollectSchemaManagersPass + */ +class CollectSchemaManagersPassTest extends TestCase +{ + /** + * Test that all collectors are found. + * + * @return void + */ + public function testProcess(): void + { + $container = new ContainerBuilder(); + + $manager = $this + ->getMockBuilder(Definition::class) + ->setMethods(['getArgument', 'setArgument']) + ->getMock(); + $manager + ->expects($this->once()) + ->method('getArgument') + ->with(0) + ->willReturn([new Reference('previous-argument')]); + $manager + ->expects($this->once()) + ->method('setArgument') + ->willReturnCallback(function ($index, $children) { + $this->assertSame(0, $index); + $this->assertCount(3, $children); + $this->assertSame('previous-argument', (string) $children[0]); + $this->assertSame('child1', (string) $children[1]); + $this->assertSame('child2', (string) $children[2]); + }); + + $container->setDefinition(SchemaManager::class, $manager); + + $child1 = new Definition(); + $child1->addTag(CollectSchemaManagersPass::TAG_NAME); + $child2 = new Definition(); + $child2->addTag(CollectSchemaManagersPass::TAG_NAME); + + $container->setDefinition('child1', $child1); + $container->setDefinition('child2', $child2); + + $pass = new CollectSchemaManagersPass(); + + $pass->process($container); + } +} diff --git a/tests/Data/FilterBuilderSqlTest.php b/tests/Data/FilterBuilderSqlTest.php index 196ce354a..f987c0092 100644 --- a/tests/Data/FilterBuilderSqlTest.php +++ b/tests/Data/FilterBuilderSqlTest.php @@ -21,7 +21,7 @@ namespace MetaModels\Test\Data; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver\ResultStatement; +use Doctrine\DBAL\Result; use MetaModels\DcGeneral\Data\FilterBuilderSql; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -35,10 +35,8 @@ class FilterBuilderSqlTest extends TestCase { /** * Test that a new builder is empty. - * - * @return void */ - public function testBuilderIsInitiallyEmpty() + public function testBuilderIsInitiallyEmpty(): void { $connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); $builder = new FilterBuilderSql('mm_test', 'AND', $connection); @@ -49,10 +47,8 @@ public function testBuilderIsInitiallyEmpty() /** * Data provider for testBuild() - * - * @return array */ - public function buildTestProvider() + public function buildTestProvider(): array { return [ 'equality compare' => [ @@ -90,11 +86,9 @@ public function buildTestProvider() * @param array $expectedParams The expected parameters. * @param array $filter The filter input array. * - * @return void - * * @dataProvider buildTestProvider */ - public function testBuild($expectedSql, $expectedParams, array $filter) + public function testBuild(string $expectedSql, array $expectedParams, array $filter): void { $connection = $this->mockConnection($expectedSql, $expectedParams, [['id' => 'succ'], ['id' => 'ess']]); $builder = new FilterBuilderSql('mm_test', 'AND', $connection); @@ -105,10 +99,8 @@ public function testBuild($expectedSql, $expectedParams, array $filter) /** * Test the build process. - * - * @return void */ - public function testBuildMultiple() + public function testBuildMultiple(): void { $connection = $this->mockConnection( 'SELECT t.id FROM mm_test AS t WHERE ((t.foo = ?) AND (t.bar = ?))', @@ -125,10 +117,8 @@ public function testBuildMultiple() /** * Test the build process. - * - * @return void */ - public function testAddSubProcedure() + public function testAddSubProcedure(): void { $child = new FilterBuilderSql( 'mm_test', @@ -157,27 +147,24 @@ public function testAddSubProcedure() * @param string $queryString The expected SQL query. * @param array $params The expected parameters. * @param array $result The query result. - * - * @return MockObject|Connection */ - private function mockConnection($queryString, $params, $result) + private function mockConnection(string $queryString, array $params, array $result): Connection&MockObject { $connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); - $statement = $this - ->getMockBuilder(ResultStatement::class) + $resultSet = $this + ->getMockBuilder(Result::class) ->disableOriginalConstructor() ->getMock(); - $statement + $resultSet ->expects(self::once()) - ->method('fetchAll') - ->with(\PDO::FETCH_ASSOC) + ->method('fetchAllAssociative') ->willReturn($result); $connection ->expects(self::once()) ->method('executeQuery') ->with($queryString, $params) - ->willReturn($statement); + ->willReturn($resultSet); return $connection; } diff --git a/tests/Data/FilterBuilderTest.php b/tests/Data/FilterBuilderTest.php index 6aad2f03a..155005769 100644 --- a/tests/Data/FilterBuilderTest.php +++ b/tests/Data/FilterBuilderTest.php @@ -22,11 +22,10 @@ use ContaoCommunityAlliance\DcGeneral\Data\DefaultConfig; use Doctrine\DBAL\Connection; -use Doctrine\DBAL\Driver\ResultStatement; +use Doctrine\DBAL\Result; use MetaModels\DcGeneral\Data\FilterBuilder; use MetaModels\IMetaModel; use MetaModels\MetaModel; -use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; use MetaModels\Attribute\Base; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -71,37 +70,6 @@ private function mockMetaModel(Connection $connection = null) return $metaModel; } - /** - * Mock a database connection with the passed query. - * - * @param string $queryString The expected SQL query. - * @param array $params The expected parameters. - * @param array $result The query result. - * - * @return MockObject|Connection - */ - private function mockConnection($queryString, $params, $result) - { - $connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); - - $statement = $this - ->getMockBuilder(ResultStatement::class) - ->disableOriginalConstructor() - ->getMock(); - $statement - ->expects(self::once()) - ->method('fetchAll') - ->with(\PDO::FETCH_ASSOC) - ->willReturn($result); - $connection - ->expects(self::once()) - ->method('executeQuery') - ->with($queryString, $params) - ->willReturn($statement); - - return $connection; - } - /** * Test the build process. * @@ -138,7 +106,7 @@ public function testBuildSqlOnly() $attribute = $this ->getMockBuilder(Base::class) ->setConstructorArgs([$metaModel, ['colname' => 'test1']]) - ->setMethods(['searchFor']) + ->onlyMethods(['searchFor']) ->getMockForAbstractClass(); $attribute @@ -167,14 +135,13 @@ public function testBuildSqlOnly() $connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); - $statement = $this - ->getMockBuilder(ResultStatement::class) + $result = $this + ->getMockBuilder(Result::class) ->disableOriginalConstructor() ->getMock(); - $statement + $result ->expects(self::once()) - ->method('fetchAll') - ->with(\PDO::FETCH_ASSOC) + ->method('fetchAllAssociative') ->willReturn([ ['id' => 0], ['id' => 1], @@ -187,13 +154,13 @@ public function testBuildSqlOnly() ->expects(self::once()) ->method('executeQuery') ->with('SELECT t.id FROM mm_test AS t WHERE ((t.foo = ?))', [0]) - ->willReturn($statement); + ->willReturn($result); $builder = new FilterBuilder($metaModel, $config, $connection); $filter = $builder->build(); - self::assertSame([0, 1, 2, 3], $filter->getMatchingIds()); + self::assertSame(['0', '1', '2', '3'], $filter->getMatchingIds()); } /** @@ -217,7 +184,7 @@ public function testIssue700() ->expects(self::once()) ->method('searchFor') ->with('*test*') - ->willReturn([0, 1, 2, 3]); + ->willReturn(['0', '1', '2', '3']); /** @var \MetaModels\Attribute\Base $attribute */ $metaModel->addAttribute($attribute); @@ -253,6 +220,6 @@ public function testIssue700() $filter = $builder->build(); - self::assertSame([0, 1, 2, 3], $filter->getMatchingIds()); + self::assertSame(['0', '1', '2', '3'], $filter->getMatchingIds()); } } diff --git a/tests/Filter/FilterUrlBuilderTest.php b/tests/Filter/FilterUrlBuilderTest.php index 416e8ec87..548075823 100644 --- a/tests/Filter/FilterUrlBuilderTest.php +++ b/tests/Filter/FilterUrlBuilderTest.php @@ -50,7 +50,6 @@ public function generateProvider(): array 'expectedUrl' => 'page-alias/auto/slug/sluggy', 'expectedParameters' => [ 'get2' => 'value', - '_locale' => 'en', ], 'page' => [ 'alias' => 'page-alias', @@ -71,7 +70,7 @@ public function generateProvider(): array 'expectedUrl' => 'alias/auto/slug/sluggy', 'expectedParameters' => [ 'get2' => 'value', - '_locale' => 'en', + 'get-param' => 'get-value', ], 'page' => [ ], @@ -134,7 +133,7 @@ public function testGenerate( $adapter = $this->getMockBuilder(Adapter::class)->disableOriginalConstructor()->getMock(); $requestStack = $this->mockRequestStack($requestGet, $requestUrl); - $builder = new FilterUrlBuilder($generator, $requestStack, true, '.html', $adapter); + $builder = new FilterUrlBuilder($generator, $requestStack, true, '.html', $adapter, true); self::assertSame('success', $builder->generate($filterUrl)); } @@ -155,20 +154,20 @@ public function testGeneratesNonStandardPorts(): void $generator ->expects(self::once()) ->method('generate') - ->with('folder/page/auto/slug/sluggy', ['get2' => 'value', '_locale' => 'en']) + ->with('folder/page/auto/slug/sluggy', ['get2' => 'value', 'get-param' => 'get-value']) ->willReturn('success'); $adapter = $this ->getMockBuilder(Adapter::class) - ->setMethods(['findByAliases']) + ->addMethods(['findByAliases']) ->disableOriginalConstructor() ->getMock(); $requestStack = $this->getMockBuilder(RequestStack::class)->getMock(); - $requestStack->method('getMasterRequest')->willReturn( + $requestStack->method('getCurrentRequest')->willReturn( new Request( ['get-param' => 'get-value'], [], - ['_locale' => 'en'], + [], [], [], [ @@ -196,7 +195,7 @@ public function testGeneratesNonStandardPorts(): void ->with(['folder/page', 'folder']) ->willReturn($pages); - $builder = new FilterUrlBuilder($generator, $requestStack, true, '.html', $adapter); + $builder = new FilterUrlBuilder($generator, $requestStack, false, '.html', $adapter, true); $prevFolderUrl = Config::get('folderUrl'); Config::set('folderUrl', true); @@ -212,13 +211,11 @@ public function testGeneratesNonStandardPorts(): void * * @param array $requestGet The current GET parameters. * @param string $requestUrl The request URL. - * - * @return RequestStack */ private function mockRequestStack(array $requestGet, string $requestUrl): RequestStack { $requestStack = $this->getMockBuilder(RequestStack::class)->getMock(); - $requestStack->method('getMasterRequest')->willReturn( + $requestStack->method('getCurrentRequest')->willReturn( new Request($requestGet, [], [], [], [], ['REQUEST_URI' => $requestUrl]) ); diff --git a/tests/Filter/Rules/SimpleQueryTest.php b/tests/Filter/Rules/SimpleQueryTest.php index 6578b4e00..aafb6b8b9 100644 --- a/tests/Filter/Rules/SimpleQueryTest.php +++ b/tests/Filter/Rules/SimpleQueryTest.php @@ -22,6 +22,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Driver\Statement; +use Doctrine\DBAL\Result; use MetaModels\Filter\Rules\SimpleQuery; use PHPUnit\Framework\TestCase; @@ -48,17 +49,20 @@ public function testExecution() ->disableOriginalConstructor() ->onlyMethods(['executeQuery']) ->getMock(); - $statement = $this->getMockForAbstractClass(Statement::class); + $result = $this + ->getMockBuilder(Result::class) + ->disableOriginalConstructor() + ->onlyMethods(['fetchAllAssociative']) + ->getMock(); $connection ->expects(self::once()) ->method('executeQuery') ->with($query, $params, $types) - ->willReturn($statement); - $statement + ->willReturn($result); + $result ->expects(self::once()) - ->method('fetchAll') - ->with(\PDO::FETCH_ASSOC) + ->method('fetchAllAssociative') ->willReturn([['idcolumn' => 'a'], ['idcolumn' => 'b'], ['idcolumn' => 'c']]); $rule = new SimpleQuery($query, $params, 'idcolumn', $connection, $types); diff --git a/tests/Filter/Setting/CustomSqlTest.php b/tests/Filter/Setting/CustomSqlTest.php index 48e8e4c74..2b186944e 100644 --- a/tests/Filter/Setting/CustomSqlTest.php +++ b/tests/Filter/Setting/CustomSqlTest.php @@ -3,7 +3,7 @@ /** * This file is part of MetaModels/core. * - * (c) 2012-2022 The MetaModels team. + * (c) 2012-2024 The MetaModels team. * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. @@ -14,7 +14,7 @@ * @author Christian Schiffler * @author Sven Baumann * @author Ingolf Steinhardt - * @copyright 2012-2022 The MetaModels team. + * @copyright 2012-2024 The MetaModels team. * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later * @filesource */ @@ -22,6 +22,7 @@ namespace MetaModels\Test\Filter\Setting; use Contao\CoreBundle\Framework\Adapter; +use Contao\CoreBundle\Framework\ContaoFramework; use Contao\Input; use Contao\PageModel; use Contao\Session as ContaoSession; @@ -52,6 +53,7 @@ * @covers \MetaModels\CoreBundle\Contao\InsertTag\ReplaceTableName * * @SuppressWarnings(PHPMD.TooManyPublicMethods) + * @SuppressWarnings(PHPMD.TooManyMethods) * @SuppressWarnings(PHPMD.CouplingBetweenObjects) */ class CustomSqlTest extends AutoLoadingTestCase @@ -64,6 +66,9 @@ class CustomSqlTest extends AutoLoadingTestCase * @param array $services The services to inject. * * @return CustomSql + * + * @SuppressWarnings(PHPMD.CyclomaticComplexity) + * @SuppressWarnings(PHPMD.NPathComplexity) */ private function mockCustomSql( array $properties = [], @@ -104,11 +109,9 @@ private function mockCustomSql( ->addMethods(['cookie', 'get', 'post']) ->getMock(); } - if (!isset($services[Session::class])) { $services[Session::class] = $this->mockSession([]); } - if (!isset($services[PageModel::class])) { $services[PageModel::class] = $this ->getMockBuilder(PageModel::class) @@ -121,6 +124,7 @@ private function mockCustomSql( $services[Request::class] = new Request(); $services[Request::class]->attributes->set('pageModel', $services[PageModel::class]); $services[Request::class]->attributes->set('_locale', $services[PageModel::class]->language); + $services[Request::class]->setSession($services[Session::class]); } if (!isset($services[RequestStack::class])) { $services[RequestStack::class] = new RequestStack(); @@ -136,13 +140,27 @@ private function mockCustomSql( ->disableOriginalConstructor() ->getMockForAbstractClass(); } - + if (!isset($services[ContaoFramework::class])) { + $services[ContaoFramework::class] = $this + ->getMockBuilder(ContaoFramework::class) + ->disableOriginalConstructor() + ->onlyMethods(['getAdapter']) + ->getMock(); + $services[ContaoFramework::class]->method('getAdapter')->willReturnCallback( + fn (string $class) => match ($class) { + Input::class => $services[Input::class], + default => throw new \RuntimeException('Override ContaoFramework instance'), + } + ); + } $services[ReplaceTableName::class] = new ReplaceTableName(); - $services[ReplaceParam::class] = new ReplaceParam($services[Input::class], $services[ContaoSession::class]); + $services[ReplaceParam::class] = new ReplaceParam( + $services[ContaoFramework::class], + $services[RequestStack::class] + ); $services[ResolveLanguageTag::class] = new ResolveLanguageTag($services[RequestStack::class]); $container = new Container(); - foreach (CustomSql::getSubscribedServices() as $serviceId) { $container->set($serviceId, $services[$serviceId]); } @@ -437,10 +455,7 @@ public function testValueFromSession(): void $setting = $this->mockCustomSql( ['customsql' => 'SELECT id FROM tableName WHERE catname={{param::session?name=category}}'], 'tableName', - [ - Session::class => $this->mockSession(['category' => 'category name']), - ContaoSession::class => $this->mockLegacySession(['category' => 'category name']), - ] + [Session::class => $this->mockSession(['category' => 'category name'])] ); $this->assertGeneratedSqlIs($setting, 'SELECT id FROM tableName WHERE catname=?', ['category name'], []); @@ -454,19 +469,13 @@ public function testValueFromSessionAggregated(): void $setting = $this->mockCustomSql( ['customsql' => 'SELECT id FROM tableName WHERE catname IN ({{param::session?name=category&aggregate}})'], 'tableName', - [ - Session::class => $this->mockSession(['category' => ['first', 'second']]), - ContaoSession::class => $this->mockLegacySession(['category' => ['first', 'second']]), - ] + [Session::class => $this->mockSession(['category' => ['first', 'second']])] ); $this->assertGeneratedSqlIs( $setting, - 'SELECT id FROM tableName WHERE catname IN (?,?)', - [ - 'first', - 'second' - ], + 'SELECT id FROM tableName WHERE catname IN (?)', + ['first,second'], [] ); } @@ -480,10 +489,7 @@ public function testValueFromSessionEmpty(): void ['customsql' => 'SELECT id FROM tableName WHERE catname={{param::session?name=category&default=defaultcat}}'], 'tableName', - [ - Session::class => $this->mockSession(['category' => null]), - ContaoSession::class => $this->mockLegacySession(['category' => null]), - ] + [Session::class => $this->mockSession(['category' => null])] ); $this->assertGeneratedSqlIs( @@ -536,11 +542,8 @@ public function testRequestParameterAggregated(): void $this->assertGeneratedSqlIs( $setting, - 'SELECT id FROM tableName WHERE catname IN (?,?)', - [ - 'first', - 'second' - ], + 'SELECT id FROM tableName WHERE catname IN (?)', + ['first,second'], [] ); } @@ -567,7 +570,7 @@ public function testRequestParameterAggregatedSet(): void [Input::class => $input] ); - $this->assertGeneratedSqlIs($setting, 'SELECT id FROM tableName WHERE catids IN (?)', ['1,2'], []); + $this->assertGeneratedSqlIs($setting, 'SELECT id FROM tableName WHERE catids IN (?,?)', ['1','2'], []); } /** @@ -707,7 +710,8 @@ public function issue1495IfLangProvider(): \Iterator ]; yield [ 'sql' => 'SELECT id FROM {{table}} -WHERE alias = {{iflng::de}}moe-yer-ss-hans-herbert-oeaeue{{iflng::en}}3{{iflng::nl}}2{{iflng::es}}4{{iflng::el}}5{{iflng}}', +WHERE alias = {{iflng::de}}moe-yer-ss-hans-herbert-oeaeue' . + '{{iflng::en}}3{{iflng::nl}}2{{iflng::es}}4{{iflng::el}}5{{iflng}}', 'language' => 'de', 'exp_sql' => 'SELECT id FROM tableName WHERE alias = moe-yer-ss-hans-herbert-oeaeue', diff --git a/tests/Helper/TableManipulatorTest.php b/tests/Helper/TableManipulatorTest.php index 6689a48d0..bccfa00f7 100644 --- a/tests/Helper/TableManipulatorTest.php +++ b/tests/Helper/TableManipulatorTest.php @@ -36,9 +36,9 @@ class TableManipulatorTest extends TestCase /** * System columns. * - * @var array + * @var list */ - private $systemColumns = [ + private array $systemColumns = [ 'id', 'pid', 'sorting', @@ -51,10 +51,8 @@ class TableManipulatorTest extends TestCase * Create the table manipulator. * * @param Connection|null $connection Optional pass a connection mock. - * - * @return TableManipulator */ - private function createTableManipulator(Connection $connection = null) + private function createTableManipulator(?Connection $connection = null): TableManipulator { $connection = $connection ?: $this->mockConnection(); @@ -63,10 +61,8 @@ private function createTableManipulator(Connection $connection = null) /** * Mock the database connection. - * - * @return MockObject|Connection */ - private function mockConnection() + private function mockConnection(): Connection&MockObject { $connection = $this->getMockBuilder(Connection::class) ->disableOriginalConstructor() @@ -77,10 +73,8 @@ private function mockConnection() /** * Test the instantiation. - * - * @return void */ - public function testInstantiation() + public function testInstantiation(): void { $manipulator = $this->createTableManipulator(); @@ -89,10 +83,8 @@ public function testInstantiation() /** * Tests the reserved words. - * - * @return void */ - public function testReservedWords() + public function testReservedWords(): void { $property = new \ReflectionProperty(TableManipulator::class, 'reservedWords'); $property->setAccessible(true); diff --git a/tests/Helper/ToolBoxFileTest.php b/tests/Helper/ToolBoxFileTest.php index fdc84845d..6a1927d4e 100644 --- a/tests/Helper/ToolBoxFileTest.php +++ b/tests/Helper/ToolBoxFileTest.php @@ -47,7 +47,7 @@ public function testConvertUuidsOrPathsToMetaModelsEmpty() 'meta' => [] ]; - self::assertEquals($emptyExpected, ToolboxFile::convertUuidsOrPathsToMetaModels(null)); + self::assertEquals($emptyExpected, ToolboxFile::convertUuidsOrPathsToMetaModels([])); self::assertEquals($emptyExpected, ToolboxFile::convertUuidsOrPathsToMetaModels([])); self::assertEquals($emptyExpected, ToolboxFile::convertUuidsOrPathsToMetaModels([null])); } diff --git a/tests/Information/AttributeInformationTest.php b/tests/Information/AttributeInformationTest.php new file mode 100644 index 000000000..3d3a2c26d --- /dev/null +++ b/tests/Information/AttributeInformationTest.php @@ -0,0 +1,47 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Information; + +use MetaModels\Information\AttributeInformation; +use PHPUnit\Framework\TestCase; + +/** + * This tests the attribute information. + * + * @covers \MetaModels\Information\AttributeInformation + */ +class AttributeInformationTest extends TestCase +{ + /** + * Test that the various methods work as intended. + * + * @return void + */ + public function testFunctionality(): void + { + $information = new AttributeInformation('attribute', 'typename', ['key1' => 'value', 'key2' => 'another']); + + $this->assertSame('attribute', $information->getName()); + $this->assertSame('typename', $information->getType()); + $this->assertSame(['key1' => 'value', 'key2' => 'another'], $information->getConfiguration()); + } +} diff --git a/tests/Information/ConfigurationTraitTest.php b/tests/Information/ConfigurationTraitTest.php new file mode 100644 index 000000000..b9085b7c7 --- /dev/null +++ b/tests/Information/ConfigurationTraitTest.php @@ -0,0 +1,68 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Information; + +use MetaModels\Information\ConfigurationTrait; +use PHPUnit\Framework\TestCase; + +/** + * This tests the configuration trait. + * + * @covers \MetaModels\Information\ConfigurationTrait + */ +class ConfigurationTraitTest extends TestCase +{ + /** + * Test that the various methods work as intended. + * + * @return void + */ + public function testFunctionality(): void + { + $trait = $this->getMockForTrait(ConfigurationTrait::class); + + $this->assertSame([], $trait->getConfiguration()); + $trait->addConfiguration(['string' => 'string', 'int' => 1, 'null' => null]); + $this->assertSame(['string' => 'string', 'int' => 1, 'null' => null], $trait->getConfiguration()); + $this->assertTrue($trait->hasConfigurationValue('string')); + $this->assertTrue($trait->hasConfigurationValue('int')); + $this->assertTrue($trait->hasConfigurationValue('null')); + $this->assertSame('string', $trait->getConfigurationValue('string')); + $this->assertSame(1, $trait->getConfigurationValue('int')); + $this->assertNull($trait->getConfigurationValue('null')); + } + + /** + * Test. + * + * @return void + */ + public function testGetConfigurationValueThrowsForUnknown(): void + { + $trait = $this->getMockForTrait(ConfigurationTrait::class); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Configuration key "unknown" does not exist'); + + $trait->getConfigurationValue('unknown'); + } +} diff --git a/tests/Information/MetaModelCollectionTest.php b/tests/Information/MetaModelCollectionTest.php new file mode 100644 index 000000000..f49934afb --- /dev/null +++ b/tests/Information/MetaModelCollectionTest.php @@ -0,0 +1,104 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Information; + +use MetaModels\Information\MetaModelInformationInterface; +use MetaModels\Information\MetaModelCollection; +use PHPUnit\Framework\TestCase; + +/** + * This tests the attribute information. + * + * @covers \MetaModels\Information\MetaModelCollection + */ +class MetaModelCollectionTest extends TestCase +{ + /** + * Test that the various methods work as intended. + * + * @return void + */ + public function testInstantiation(): void + { + $information = new MetaModelCollection(); + $this->assertSame([], $information->getNames()); + $this->assertSame([], $information->all()); + $this->assertSame([], iterator_to_array($information)); + } + + /** + * Test adding of a MetaModel. + * + * @return void + */ + public function testAddMetaModel(): void + { + $information = new MetaModelCollection(); + + $metamodel = $this->getMockForAbstractClass(MetaModelInformationInterface::class); + $metamodel->expects($this->once())->method('getName')->willReturn('mm_test'); + + $information->add($metamodel); + + $this->assertTrue($information->has('mm_test')); + $this->assertSame($metamodel, $information->get('mm_test')); + $this->assertSame([$metamodel], $information->all()); + $this->assertSame([$metamodel], iterator_to_array($information)); + } + + /** + * Test adding twice. + * + * @return void + */ + public function testAddThrowsForRegisteredName(): void + { + $information = new MetaModelCollection(); + + $metamodel = $this->getMockForAbstractClass(MetaModelInformationInterface::class); + $metamodel->expects($this->once())->method('getName')->willReturn('mm_test'); + $information->add($metamodel); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('MetaModel "mm_test" already registered'); + + $second = $this->getMockForAbstractClass(MetaModelInformationInterface::class); + $second->expects($this->once())->method('getName')->willReturn('mm_test'); + + $information->add($second); + } + + /** + * Test fetching unknown attribute. + * + * @return void + */ + public function testGetThrowsForUnknown(): void + { + $information = new MetaModelCollection(); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Unknown MetaModel "unknown"'); + + $information->get('unknown'); + } +} diff --git a/tests/Information/MetaModelInformationTest.php b/tests/Information/MetaModelInformationTest.php new file mode 100644 index 000000000..1e5f72d77 --- /dev/null +++ b/tests/Information/MetaModelInformationTest.php @@ -0,0 +1,134 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Information; + +use MetaModels\Information\AttributeInformationInterface; +use MetaModels\Information\MetaModelInformation; +use PHPUnit\Framework\TestCase; + +/** + * This tests the attribute information. + * + * @covers \MetaModels\Information\MetaModelInformation + */ +class MetaModelInformationTest extends TestCase +{ + /** + * Test that the various methods work as intended. + * + * @return void + */ + public function testFunctionality(): void + { + $information = new MetaModelInformation('mm_test', ['key1' => 'value', 'key2' => 'another']); + + $this->assertSame('mm_test', $information->getName()); + $this->assertSame(['key1' => 'value', 'key2' => 'another'], $information->getConfiguration()); + $this->assertSame([], $information->getAttributeNames()); + $this->assertSame([], $information->getAttributes()); + } + + /** + * Test adding of an attribute. + * + * @return void + */ + public function testAddAttribute(): void + { + $information = new MetaModelInformation('mm_test'); + + $attribute = $this->getMockForAbstractClass(AttributeInformationInterface::class); + $attribute->expects($this->once())->method('getName')->willReturn('test'); + + $information->addAttribute($attribute); + + $this->assertTrue($information->hasAttribute('test')); + $this->assertSame($attribute, $information->getAttribute('test')); + $this->assertSame([$attribute], $information->getAttributes()); + } + + /** + * Test adding twice. + * + * @return void + */ + public function testAddThrowsForRegisteredName(): void + { + $information = new MetaModelInformation('mm_test'); + + $attribute = $this->getMockForAbstractClass(AttributeInformationInterface::class); + $attribute->expects($this->once())->method('getName')->willReturn('test'); + $information->addAttribute($attribute); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Attribute "test" already registered'); + + $second = $this->getMockForAbstractClass(AttributeInformationInterface::class); + $second->expects($this->once())->method('getName')->willReturn('test'); + + $information->addAttribute($second); + } + + /** + * Test fetching unknown attribute. + * + * @return void + */ + public function testGetAttributeThrowsForUnknown(): void + { + $information = new MetaModelInformation('mm_test'); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Unknown attribute "unknown"'); + + $information->getAttribute('unknown'); + } + + /** + * Test retrieving attributes of a given type. + * + * @return void + */ + public function testGetAttributesOfType(): void + { + $information = new MetaModelInformation('mm_test'); + + $attribute1 = $this->getMockForAbstractClass(AttributeInformationInterface::class); + $attribute1->expects($this->once())->method('getName')->willReturn('test1'); + $attribute1->expects($this->once())->method('getType')->willReturn('searched'); + $information->addAttribute($attribute1); + $attribute2 = $this->getMockForAbstractClass(AttributeInformationInterface::class); + $attribute2->expects($this->once())->method('getName')->willReturn('test2'); + $attribute2->expects($this->once())->method('getType')->willReturn('other'); + $information->addAttribute($attribute2); + $attribute3 = $this->getMockForAbstractClass(AttributeInformationInterface::class); + $attribute3->expects($this->once())->method('getName')->willReturn('test3'); + $attribute3->expects($this->once())->method('getType')->willReturn('searched'); + + $information->addAttribute($attribute3); + + $this->assertSame( + [$attribute1, $attribute3], + iterator_to_array($information->getAttributesOfType('searched')) + ); + } +} diff --git a/tests/InformationProvider/MetaModelInformationCollectorTest.php b/tests/InformationProvider/MetaModelInformationCollectorTest.php new file mode 100644 index 000000000..19e9a1bd1 --- /dev/null +++ b/tests/InformationProvider/MetaModelInformationCollectorTest.php @@ -0,0 +1,105 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\InformationProvider; + +use MetaModels\Information\MetaModelInformation; +use MetaModels\InformationProvider\MetaModelInformationCollector; +use MetaModels\InformationProvider\InformationProviderInterface; +use PHPUnit\Framework\TestCase; + +/** + * This tests the information collector. + * + * @covers \MetaModels\InformationProvider\MetaModelInformationCollector + */ +class MetaModelInformationCollectorTest extends TestCase +{ + /** + * Test the class can be instantiated. + * + * @return void + */ + public function testInstantiation(): void + { + $collector = new MetaModelInformationCollector([]); + $this->assertInstanceOf(MetaModelInformationCollector::class, $collector); + } + + /** + * Test that all providers are queried. + * + * @return void + */ + public function testCollectsNamesFromAllProviders(): void + { + $provider1 = $this->getMockForAbstractClass(InformationProviderInterface::class); + $provider2 = $this->getMockForAbstractClass(InformationProviderInterface::class); + + $provider1->expects($this->once())->method('getNames')->willReturn(['name1', 'name2']); + $provider2->expects($this->once())->method('getNames')->willReturn(['name2', 'name3']); + + $collector = new MetaModelInformationCollector([$provider1, $provider2]); + + $this->assertSame(['name1', 'name2', 'name3'], $collector->getNames()); + } + + /** + * Test that all providers are queried. + * + * @return void + */ + public function testCollectsInformationFromAllProviders(): void + { + $information = new MetaModelInformation('mm_test'); + + $provider1 = $this->getMockForAbstractClass(InformationProviderInterface::class); + $provider2 = $this->getMockForAbstractClass(InformationProviderInterface::class); + + $provider1->expects($this->once())->method('getInformationFor')->with($information); + $provider2->expects($this->once())->method('getInformationFor')->with($information); + + $collector = new MetaModelInformationCollector([$provider1, $provider2]); + + $collector->getInformationFor($information); + } + + /** + * Test obtaining of the collection. + * + * @return void + */ + public function testGetCollection(): void + { + $collector = $this + ->getMockBuilder(MetaModelInformationCollector::class) + ->setMethods(['getNames', 'getInformationFor']) + ->disableOriginalConstructor() + ->getMock(); + + $collector->expects($this->once())->method('getNames')->willReturn(['name1', 'name2', 'name3']); + $collector->expects($this->exactly(3))->method('getInformationFor'); + /** @var MetaModelInformationCollector $collector */ + $collection = $collector->getCollection(); + + $this->assertSame(['name1', 'name2', 'name3'], $collection->getNames()); + } +} diff --git a/tests/ItemListTest.php b/tests/ItemListTest.php index 4e6bd0ca1..329cc1676 100644 --- a/tests/ItemListTest.php +++ b/tests/ItemListTest.php @@ -23,12 +23,15 @@ namespace MetaModels\Test; +use Contao\CoreBundle\Routing\ScopeMatcher; +use Contao\System; use MetaModels\Filter\Setting\IFilterSettingFactory; use MetaModels\IFactory; use MetaModels\ItemList; use MetaModels\Filter\FilterUrlBuilder; use MetaModels\Render\Setting\IRenderSettingFactory; use PHPUnit\Framework\TestCase; +use Symfony\Component\DependencyInjection\ContainerInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -38,6 +41,7 @@ */ final class ItemListTest extends TestCase { + /** @SuppressWarnings(PHPMD.Superglobals) */ public function testGetOutputFormat(): void { $factory = $this->getMockForAbstractClass(IFactory::class); @@ -45,7 +49,13 @@ public function testGetOutputFormat(): void $renderSettingFactory = $this->getMockForAbstractClass(IRenderSettingFactory::class); $eventDispatcher = $this->getMockForAbstractClass(EventDispatcherInterface::class); $filterUrlBuilder = $this->getMockBuilder(FilterUrlBuilder::class)->disableOriginalConstructor()->getMock(); - $itemlist = new ItemList($factory, $filterFactory, $renderSettingFactory, $eventDispatcher, $filterUrlBuilder); + $itemlist = new ItemList( + $factory, + $filterFactory, + $renderSettingFactory, + $eventDispatcher, + $filterUrlBuilder + ); if (!defined('TL_MODE')) { define('TL_MODE', 'FE'); @@ -55,6 +65,22 @@ public function testGetOutputFormat(): void self::markTestSkipped('Test assumes that TL_MODE is set to "FE"'); } + $scopeMatcher = $this + ->getMockBuilder(ScopeMatcher::class) + ->onlyMethods(['isFrontendRequest']) + ->disableOriginalConstructor() + ->getMock(); + $scopeMatcher->method('isFrontendRequest')->willReturn(true); + + $mockContainer = $this->getMockForAbstractClass(ContainerInterface::class); + $mockContainer + ->method('get') + ->willReturnCallback(fn(string $service) => match ($service) { + 'contao.routing.scope_matcher' => $scopeMatcher, + 'request_stack' => null, + }); + System::setContainer($mockContainer); + $GLOBALS['objPage'] = null; self::assertSame('text', $itemlist->getOutputFormat()); diff --git a/tests/MetaModelsTest.php b/tests/MetaModelsTest.php index 07fc89d0d..4f91d560a 100644 --- a/tests/MetaModelsTest.php +++ b/tests/MetaModelsTest.php @@ -23,7 +23,7 @@ use Doctrine\DBAL\Connection; use Doctrine\DBAL\Query\Expression\ExpressionBuilder; use Doctrine\DBAL\Query\QueryBuilder; -use Doctrine\DBAL\Statement; +use Doctrine\DBAL\Result; use MetaModels\MetaModel; use PHPUnit\Framework\MockObject\MockObject; use PHPUnit\Framework\TestCase; @@ -38,12 +38,10 @@ class MetaModelsTest extends TestCase { /** * Test instantiation of a MetaModel. - * - * @return void */ - public function testCreation() + public function testCreation(): void { - $values = array( + $values = [ 'id' => '1', 'sorting' => '1', 'tstamp' => '0', @@ -51,12 +49,12 @@ public function testCreation() 'tableName' => 'mm_test', 'mode' => '', 'translated' => '1', - 'languages' => array( - 'en' => array('isfallback' => '1'), - 'de' => array('isfallback' => '') - ), + 'languages' => [ + 'en' => ['isfallback' => '1'], + 'de' => ['isfallback' => ''] + ], 'varsupport' => '1', - ); + ]; $serialized = array(); foreach ($values as $key => $value) { @@ -67,14 +65,22 @@ public function testCreation() } } - $metaModel = new MetaModel($serialized); + $metaModel = new MetaModel( + $serialized, + $this->getMockForAbstractClass(EventDispatcherInterface::class), + $this->mockConnection() + ); self::assertEmpty($metaModel->getAttributes()); foreach ($values as $key => $value) { self::assertEquals($value, $metaModel->get($key), $key); } - $metaModel = new MetaModel($values); + $metaModel = new MetaModel( + $values, + $this->getMockForAbstractClass(EventDispatcherInterface::class), + $this->mockConnection() + ); foreach ($values as $key => $value) { self::assertEquals($value, $metaModel->get($key), $key); @@ -83,26 +89,26 @@ public function testCreation() /** * Ensure the buildDatabaseParameterList works correctly. - * - * @return void */ - public function testBuildDatabaseParameterList() + public function testBuildDatabaseParameterList(): void { - $metaModel = new MetaModel(array()); + $metaModel = new MetaModel( + [], + $this->getMockForAbstractClass(EventDispatcherInterface::class), + $this->mockConnection() + ); $reflection = new \ReflectionMethod($metaModel, 'buildDatabaseParameterList'); $reflection->setAccessible(true); - self::assertEquals('?', $reflection->invoke($metaModel, array(1))); - self::assertEquals('?,?', $reflection->invoke($metaModel, array(1, 2))); - self::assertEquals('?,?,?,?,?,?', $reflection->invoke($metaModel, array(1, 2, 'fooo', 'bar', null, 'test'))); + self::assertEquals('?', $reflection->invoke($metaModel, [1])); + self::assertEquals('?,?', $reflection->invoke($metaModel, [1, 2])); + self::assertEquals('?,?,?,?,?,?', $reflection->invoke($metaModel, [1, 2, 'fooo', 'bar', null, 'test'])); } /** * Ensure the system columns are present. See issue #196. - * - * @return void */ - public function testRetrieveSystemColumns() + public function testRetrieveSystemColumns(): void { $metaModel = new MetaModel( [ @@ -116,8 +122,8 @@ public function testRetrieveSystemColumns() 'varsupport' => '', ], $this->getMockForAbstractClass(EventDispatcherInterface::class), - $this->mockConnection([ - \Closure::fromCallable(function () { + $this->mockConnection( + (function () { $builder = $this ->getMockBuilder(QueryBuilder::class) ->disableOriginalConstructor() @@ -136,7 +142,7 @@ public function testRetrieveSystemColumns() $expr = $this ->getMockBuilder(ExpressionBuilder::class) ->disableOriginalConstructor() - ->setMethods() + ->onlyMethods([]) ->getMock(); $builder @@ -162,31 +168,30 @@ public function testRetrieveSystemColumns() ->with('FIELD(id, :values)') ->willReturn($builder); - $statement = $this - ->getMockBuilder(Statement::class) + $result = $this + ->getMockBuilder(Result::class) ->disableOriginalConstructor() ->getMock(); - $statement + $result ->expects($this->exactly(2)) - ->method('fetch') - ->with(\PDO::FETCH_ASSOC) + ->method('fetchAssociative') ->willReturnOnConsecutiveCalls([ 'id' => 1, 'pid' => 0, 'sorting' => 1, 'tstamp' => 343094400, - ], null); + ], false); $builder ->expects($this->once()) - ->method('execute') - ->willReturn($statement); + ->method('executeQuery') + ->willReturn($result); return $builder; })->__invoke() - ]) + ) ); - self::assertEquals($metaModel->getName(), 'Test RetrieveSystemColumns'); + self::assertEquals('Test RetrieveSystemColumns', $metaModel->getName()); $item = $metaModel->findById(1); @@ -200,18 +205,16 @@ public function testRetrieveSystemColumns() /** * Ensure the getIdsFromFilter works correctly. - * - * @return void */ - public function testGetIdsFromFilterSortedById() + public function testGetIdsFromFilterSortedById(): void { $metaModel = $this ->getMockBuilder(MetaModel::class) - ->setMethods(['getMatchingIds']) + ->onlyMethods(['getMatchingIds']) ->setConstructorArgs([ ['tableName' => 'mm_test_retrieve'], $this->getMockForAbstractClass(EventDispatcherInterface::class), - $this->mockConnection([]) + $this->mockConnection() ]) ->getMock(); $metaModel @@ -219,7 +222,6 @@ public function testGetIdsFromFilterSortedById() ->method('getMatchingIds') ->willReturn([4, 3, 2, 1]); - /** @var MetaModel $metaModel */ self::assertSame([1, 2, 3, 4], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'id')); self::assertSame([1, 2], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'id', 0, 2)); self::assertSame([3, 4], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'id', 2, 2)); @@ -230,19 +232,17 @@ public function testGetIdsFromFilterSortedById() /** * Ensure the getIdsFromFilter works correctly when sorting by pid and slicing the results. - * - * @return void */ - public function testGetIdsFromFilterSortedByPid() + public function testGetIdsFromFilterSortedByPid(): void { $metaModel = $this ->getMockBuilder(MetaModel::class) - ->setMethods(['getMatchingIds']) + ->onlyMethods(['getMatchingIds']) ->setConstructorArgs([ ['tableName' => 'mm_test_retrieve'], $this->getMockForAbstractClass(EventDispatcherInterface::class), - $this->mockConnection([ - \Closure::fromCallable(function () { + $this->mockConnection( + (function () { $builder = $this ->getMockBuilder(QueryBuilder::class) ->disableOriginalConstructor() @@ -261,7 +261,7 @@ public function testGetIdsFromFilterSortedByPid() $expr = $this ->getMockBuilder(ExpressionBuilder::class) ->disableOriginalConstructor() - ->setMethods() + ->onlyMethods([]) ->getMock(); $builder @@ -287,23 +287,22 @@ public function testGetIdsFromFilterSortedByPid() ->with('pid', 'ASC') ->willReturn($builder); - $statement = $this - ->getMockBuilder(Statement::class) + $result = $this + ->getMockBuilder(Result::class) ->disableOriginalConstructor() ->getMock(); - $statement + $result ->expects($this->once()) - ->method('fetchAll') - ->with(\PDO::FETCH_COLUMN) + ->method('fetchFirstColumn') ->willReturn([1, 2, 3, 4]); $builder ->expects($this->once()) - ->method('execute') - ->willReturn($statement); + ->method('executeQuery') + ->willReturn($result); return $builder; })->__invoke() - ]) + ) ]) ->getMock(); $metaModel @@ -311,30 +310,27 @@ public function testGetIdsFromFilterSortedByPid() ->method('getMatchingIds') ->willReturn([4, 3, 2, 1]); - /** @var MetaModel $metaModel */ - self::assertSame(array(1, 2, 3, 4), $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid')); - self::assertSame(array(1, 2), $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid', 0, 2)); - self::assertSame(array(3, 4), $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid', 2, 2)); - self::assertSame(array(3), $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid', 2, 1)); - self::assertSame(array(), $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid', 20, 0)); - self::assertSame(array(2, 3, 4), $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid', 1, 10)); + self::assertSame([1, 2, 3, 4], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid')); + self::assertSame([1, 2], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid', 0, 2)); + self::assertSame([3, 4], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid', 2, 2)); + self::assertSame([3], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid', 2, 1)); + self::assertSame([], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid', 20, 0)); + self::assertSame([2, 3, 4], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid', 1, 10)); } /** * Ensure the getIdsFromFilter works correctly when the results have been cached. - * - * @return void */ - public function testGetIdsFromFilterSortedByPidWithCache() + public function testGetIdsFromFilterSortedByPidWithCache(): void { $metaModel = $this ->getMockBuilder(MetaModel::class) - ->setMethods(['getMatchingIds']) + ->onlyMethods(['getMatchingIds']) ->setConstructorArgs([ ['tableName' => 'mm_test_retrieve'], $this->getMockForAbstractClass(EventDispatcherInterface::class), - $this->mockConnection([ - \Closure::fromCallable(function () { + $this->mockConnection( + (function () { $builder = $this ->getMockBuilder(QueryBuilder::class) ->disableOriginalConstructor() @@ -353,7 +349,7 @@ public function testGetIdsFromFilterSortedByPidWithCache() $expr = $this ->getMockBuilder(ExpressionBuilder::class) ->disableOriginalConstructor() - ->setMethods() + ->onlyMethods([]) ->getMock(); $builder @@ -379,23 +375,22 @@ public function testGetIdsFromFilterSortedByPidWithCache() ->with('pid', 'ASC') ->willReturn($builder); - $statement = $this - ->getMockBuilder(Statement::class) + $result = $this + ->getMockBuilder(Result::class) ->disableOriginalConstructor() ->getMock(); - $statement + $result ->expects($this->once()) - ->method('fetchAll') - ->with(\PDO::FETCH_COLUMN) + ->method('fetchFirstColumn') ->willReturn([1, 2, 3, 4]); $builder ->expects($this->once()) - ->method('execute') - ->willReturn($statement); + ->method('executeQuery') + ->willReturn($result); return $builder; })->__invoke() - ]) + ) ]) ->getMock(); $metaModel @@ -403,26 +398,23 @@ public function testGetIdsFromFilterSortedByPidWithCache() ->method('getMatchingIds') ->willReturnOnConsecutiveCalls([4, 3, 2, 1], [3, 2]); - /** @var MetaModel $metaModel */ self::assertSame([1, 2, 3, 4], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid')); self::assertSame([2, 3], $metaModel->getIdsFromFilter($metaModel->getEmptyFilter(), 'pid')); } /** * Ensure the getCount works correctly. - * - * @return void */ - public function testGetCountForEmptyList() + public function testGetCountForEmptyList(): void { $metaModel = $this ->getMockBuilder(MetaModel::class) - ->setMethods(['getMatchingIds']) + ->onlyMethods(['getMatchingIds']) ->setConstructorArgs( [ ['tableName' => 'mm_test_retrieve'], $this->getMockForAbstractClass(EventDispatcherInterface::class), - $this->mockConnection([]) + $this->mockConnection() ] ) ->getMock(); @@ -437,18 +429,16 @@ public function testGetCountForEmptyList() /** * Ensure the getCount works correctly. - * - * @return void */ - public function testGetCountForNonEmptyList() + public function testGetCountForNonEmptyList(): void { $metaModel = $this->getMockBuilder(MetaModel::class) - ->setMethods(['getMatchingIds']) + ->onlyMethods(['getMatchingIds']) ->setConstructorArgs([ ['tableName' => 'mm_test_retrieve'], $this->getMockForAbstractClass(EventDispatcherInterface::class), - $this->mockConnection([ - \Closure::fromCallable(function () { + $this->mockConnection( + (function () { $builder = $this ->getMockBuilder(QueryBuilder::class) ->disableOriginalConstructor() @@ -467,7 +457,7 @@ public function testGetCountForNonEmptyList() $expr = $this ->getMockBuilder(ExpressionBuilder::class) ->disableOriginalConstructor() - ->setMethods() + ->onlyMethods([]) ->getMock(); $builder @@ -487,23 +477,22 @@ public function testGetCountForNonEmptyList() ->with('values', [4, 3, 2, 1], Connection::PARAM_STR_ARRAY) ->willReturn($builder); - $statement = $this - ->getMockBuilder(Statement::class) + $result = $this + ->getMockBuilder(Result::class) ->disableOriginalConstructor() ->getMock(); - $statement + $result ->expects($this->once()) - ->method('fetch') - ->with(\PDO::FETCH_COLUMN) + ->method('fetchOne') ->willReturn(4); $builder ->expects($this->once()) - ->method('execute') - ->willReturn($statement); + ->method('executeQuery') + ->willReturn($result); return $builder; })->__invoke() - ]) + ) ]) ->getMock(); $metaModel @@ -511,18 +500,15 @@ public function testGetCountForNonEmptyList() ->method('getMatchingIds') ->willReturn([4, 3, 2, 1]); - /** @var MetaModel $metaModel */ self::assertEquals(4, $metaModel->getCount($metaModel->getEmptyFilter())); } /** * Mock a database connection with hte passed query builders. * - * @param array $queryBuilders The query builder list. - * - * @return MockObject|Connection + * @param QueryBuilder ...$queryBuilders The query builder list. */ - private function mockConnection(array $queryBuilders) + private function mockConnection(QueryBuilder ...$queryBuilders): Connection&MockObject { $connection = $this->getMockBuilder(Connection::class)->disableOriginalConstructor()->getMock(); diff --git a/tests/Render/TemplateTest.php b/tests/Render/TemplateTest.php index 54f92abd8..ae153c757 100644 --- a/tests/Render/TemplateTest.php +++ b/tests/Render/TemplateTest.php @@ -130,6 +130,8 @@ public function testCacheOverMultipleInstances(): void * Test different caches for custom paths. * * @return void + * + * @SuppressWarnings(PHPMD.Superglobals) */ public function testCacheForEachCustomPathInstances(): void { diff --git a/tests/Schema/Doctrine/DoctrineSchemaGeneratorTest.php b/tests/Schema/Doctrine/DoctrineSchemaGeneratorTest.php new file mode 100644 index 000000000..fabe5fba2 --- /dev/null +++ b/tests/Schema/Doctrine/DoctrineSchemaGeneratorTest.php @@ -0,0 +1,89 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Schema\Doctrine; + +use MetaModels\Information\MetaModelCollectionInterface; +use MetaModels\Schema\Doctrine\DoctrineSchemaGenerator; +use MetaModels\Schema\Doctrine\DoctrineSchemaInformation; +use MetaModels\Schema\Doctrine\DoctrineSchemaGeneratorInterface; +use MetaModels\Schema\SchemaInformation; +use PHPUnit\Framework\TestCase; + +/** + * This tests the doctrine schema. + * + * @covers \MetaModels\Schema\Doctrine\DoctrineSchemaGenerator + */ +class DoctrineSchemaGeneratorTest extends TestCase +{ + /** + * Test the instantiation. + * + * @return void + */ + public function testInstantiation(): void + { + $instance = new DoctrineSchemaGenerator([]); + + $this->assertInstanceOf(DoctrineSchemaGenerator::class, $instance); + } + + /** + * Test the generate method. + * + * @return void + */ + public function testGenerateAddsSchemaInformationIfNotFound(): void + { + $instance = new DoctrineSchemaGenerator([]); + + $information = new SchemaInformation(); + + $collection = $this->getMockForAbstractClass(MetaModelCollectionInterface::class); + + $instance->generate($information, $collection); + + $this->assertTrue($information->has(DoctrineSchemaInformation::class)); + } + + /** + * Test the generate method. + * + * @return void + */ + public function testGenerate(): void + { + $doctrineSchema = new DoctrineSchemaInformation(); + $collection = $this->getMockForAbstractClass(MetaModelCollectionInterface::class); + $generator1 = $this->getMockForAbstractClass(DoctrineSchemaGeneratorInterface::class); + $generator2 = $this->getMockForAbstractClass(DoctrineSchemaGeneratorInterface::class); + $generator1->expects($this->once())->method('generate')->with($doctrineSchema, $collection); + $generator2->expects($this->once())->method('generate')->with($doctrineSchema, $collection); + + + $instance = new DoctrineSchemaGenerator([$generator1, $generator2]); + + $information = new SchemaInformation(); + + $instance->generate($information, $collection); + } +} diff --git a/tests/Schema/Doctrine/DoctrineSchemaInformationTest.php b/tests/Schema/Doctrine/DoctrineSchemaInformationTest.php new file mode 100644 index 000000000..bb9765576 --- /dev/null +++ b/tests/Schema/Doctrine/DoctrineSchemaInformationTest.php @@ -0,0 +1,115 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Schema\Doctrine; + +use Doctrine\DBAL\Schema\Schema; +use MetaModels\Schema\Doctrine\DoctrineSchemaInformation; +use MetaModels\Schema\Doctrine\SchemaProcessorInterface; +use PHPUnit\Framework\TestCase; + +/** + * This tests the doctrine schema. + * + * @covers \MetaModels\Schema\Doctrine\DoctrineSchemaInformation + */ +class DoctrineSchemaInformationTest extends TestCase +{ + /** + * Test the instantiation. + * + * @return void + */ + public function testInstantiation(): void + { + $schema = new Schema(); + + $instance = new DoctrineSchemaInformation($schema); + + $this->assertInstanceOf(DoctrineSchemaInformation::class, $instance); + $this->assertSame(DoctrineSchemaInformation::class, $instance->getName()); + $this->assertSame($schema, $instance->getSchema()); + $this->assertSame([], $instance->getPreProcessors()); + $this->assertSame([], $instance->getPostProcessors()); + } + + /** + * Test the pre processor handling. + * + * @return void + */ + public function testAddPreProcessors(): void + { + $schema = new Schema(); + + $processorNormal = $this + ->getMockBuilder(SchemaProcessorInterface::class) + ->setMockClassName('TestAddPreProcessorsNormal') + ->getMockForAbstractClass(); + $processorHigh = $this + ->getMockBuilder(SchemaProcessorInterface::class) + ->setMockClassName('TestAddPreProcessorsHigh') + ->getMockForAbstractClass(); + $processorLow = $this + ->getMockBuilder(SchemaProcessorInterface::class) + ->setMockClassName('TestAddPreProcessorsLow') + ->getMockForAbstractClass(); + + $instance = new DoctrineSchemaInformation($schema); + + $instance->addPreProcessor($processorNormal); + $instance->addPreProcessor($processorHigh, 100); + $instance->addPreProcessor($processorLow, -100); + + $this->assertSame([$processorHigh, $processorNormal, $processorLow], $instance->getPreProcessors()); + } + + /** + * Test the post processor handling. + * + * @return void + */ + public function testAddPostProcessors(): void + { + $schema = new Schema(); + + $processorNormal = $this + ->getMockBuilder(SchemaProcessorInterface::class) + ->setMockClassName('TestAddPostProcessorsNormal') + ->getMockForAbstractClass(); + $processorHigh = $this + ->getMockBuilder(SchemaProcessorInterface::class) + ->setMockClassName('TestAddPostProcessorsHigh') + ->getMockForAbstractClass(); + $processorLow = $this + ->getMockBuilder(SchemaProcessorInterface::class) + ->setMockClassName('TestAddPostProcessorsLow') + ->getMockForAbstractClass(); + + $instance = new DoctrineSchemaInformation($schema); + + $instance->addPostProcessor($processorNormal); + $instance->addPostProcessor($processorHigh, 100); + $instance->addPostProcessor($processorLow, -100); + + $this->assertSame([$processorHigh, $processorNormal, $processorLow], $instance->getPostProcessors()); + } +} diff --git a/tests/Schema/Doctrine/DoctrineSchemaManagerTest.php b/tests/Schema/Doctrine/DoctrineSchemaManagerTest.php new file mode 100644 index 000000000..33d84a442 --- /dev/null +++ b/tests/Schema/Doctrine/DoctrineSchemaManagerTest.php @@ -0,0 +1,285 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Schema\Doctrine; + +use MetaModels\Schema\Doctrine\DoctrineSchemaInformation; +use MetaModels\Schema\Doctrine\DoctrineSchemaManager; +use MetaModels\Schema\Doctrine\DoctrineSchemaManipulator; +use MetaModels\Schema\Doctrine\SchemaProcessorInterface; +use MetaModels\Schema\SchemaInformation; +use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\TestCase; + +/** + * This tests the doctrine schema. + * + * @covers \MetaModels\Schema\Doctrine\DoctrineSchemaManager + */ +class DoctrineSchemaManagerTest extends TestCase +{ + /** + * Test the instantiation. + * + * @return void + */ + public function testInstantiation(): void + { + /** @var MockObject|DoctrineSchemaManipulator $manipulator */ + $manipulator = $this->getMockBuilder(DoctrineSchemaManipulator::class)->disableOriginalConstructor()->getMock(); + $instance = new DoctrineSchemaManager($manipulator); + + $this->assertInstanceOf(DoctrineSchemaManager::class, $instance); + } + + /** + * Test the preprocess method. + * + * @return void + */ + public function testPreprocess(): void + { + $processor1 = $this->getMockForAbstractClass(SchemaProcessorInterface::class); + $processor2 = $this->getMockForAbstractClass(SchemaProcessorInterface::class); + + $processor1->expects($this->once())->method('process'); + $processor2->expects($this->once())->method('process'); + + /** @var MockObject|DoctrineSchemaInformation $information */ + $information = $this + ->getMockBuilder(DoctrineSchemaInformation::class) + ->disableOriginalConstructor() + ->getMock(); + $information + ->expects($this->once()) + ->method('getPreProcessors') + ->willReturn([$processor1, $processor2]); + + $schemaInformation = $this->mockSchemaInformation($information); + + $instance = $this->createSchemaManager(); + $instance->preprocess($schemaInformation); + } + + /** + * Test the preprocess method. + * + * @return void + */ + public function testPreprocessSkipsIfNothingToDo(): void + { + $schemaInformation = $this->mockSchemaInformation(); + + $instance = $this->createSchemaManager(); + $instance->preprocess($schemaInformation); + } + + /** + * Test the process method. + * + * @return void + */ + public function testProcess(): void + { + $manipulator = $this + ->getMockBuilder(DoctrineSchemaManipulator::class) + ->disableOriginalConstructor() + ->getMock(); + + /** @var MockObject|DoctrineSchemaInformation $information */ + $information = $this + ->getMockBuilder(DoctrineSchemaInformation::class) + ->disableOriginalConstructor() + ->getMock(); + + $manipulator->expects($this->once())->method('updateDatabase')->with($information); + + $instance = $this->createSchemaManager($manipulator); + + $schemaInformation = $this->mockSchemaInformation($information); + $instance->process($schemaInformation); + } + + /** + * Test the postprocess method. + * + * @return void + */ + public function testProcessSkipsIfNothingToDo(): void + { + $schemaInformation = $this->mockSchemaInformation(); + + $instance = $this->createSchemaManager(); + $instance->process($schemaInformation); + } + + /** + * Test the postprocess method. + * + * @return void + */ + public function testPostProcess(): void + { + $processor1 = $this->getMockForAbstractClass(SchemaProcessorInterface::class); + $processor2 = $this->getMockForAbstractClass(SchemaProcessorInterface::class); + + $processor1->expects($this->once())->method('process'); + $processor2->expects($this->once())->method('process'); + + /** @var MockObject|DoctrineSchemaInformation $information */ + $information = $this + ->getMockBuilder(DoctrineSchemaInformation::class) + ->disableOriginalConstructor() + ->getMock(); + $information + ->expects($this->once()) + ->method('getPostProcessors') + ->willReturn([$processor1, $processor2]); + + $schemaInformation = $this->mockSchemaInformation($information); + + $instance = $this->createSchemaManager(); + $instance->postprocess($schemaInformation); + } + + /** + * Test the postprocess method. + * + * @return void + */ + public function testPostprocessSkipsIfNothingToDo(): void + { + $schemaInformation = $this->mockSchemaInformation(); + + $instance = $this->createSchemaManager(); + $instance->postprocess($schemaInformation); + } + + /** + * Test the validation + * + * @return void + */ + public function testValidate(): void + { + $manipulator = $this + ->getMockBuilder(DoctrineSchemaManipulator::class) + ->disableOriginalConstructor() + ->getMock(); + + $manipulator->expects($this->once())->method('getScript')->willReturn(['query1', 'query2']); + + $processor1 = $this->getMockForAbstractClass(SchemaProcessorInterface::class); + $processor2 = $this->getMockForAbstractClass(SchemaProcessorInterface::class); + $processor1->expects($this->once())->method('__toString')->willReturn('pre1'); + $processor2->expects($this->once())->method('__toString')->willReturn('pre2'); + $processor3 = $this->getMockForAbstractClass(SchemaProcessorInterface::class); + $processor4 = $this->getMockForAbstractClass(SchemaProcessorInterface::class); + $processor3->expects($this->once())->method('__toString')->willReturn('post1'); + $processor4->expects($this->once())->method('__toString')->willReturn('post2'); + + /** @var MockObject|DoctrineSchemaInformation $information */ + $information = $this + ->getMockBuilder(DoctrineSchemaInformation::class) + ->disableOriginalConstructor() + ->getMock(); + $information + ->expects($this->once()) + ->method('getPreProcessors') + ->willReturn([$processor1, $processor2]); + $information + ->expects($this->once()) + ->method('getPostProcessors') + ->willReturn([$processor3, $processor4]); + + $schemaInformation = $this->mockSchemaInformation($information); + + $instance = $this->createSchemaManager($manipulator); + + $this->assertSame([ + 'pre1', + 'pre2', + 'Execute SQL: query1', + 'Execute SQL: query2', + 'post1', + 'post2', + ], $instance->validate($schemaInformation)); + } + + /** + * Create a schema manager instance. + * + * @param MockObject|DoctrineSchemaManipulator|null $manipulator The manipulator. + * + * @return DoctrineSchemaManager + */ + private function createSchemaManager( + DoctrineSchemaManipulator $manipulator = null + ): DoctrineSchemaManager { + if (null === $manipulator) { + $manipulator = $this + ->getMockBuilder(DoctrineSchemaManipulator::class) + ->disableOriginalConstructor() + ->getMock(); + } + + return new DoctrineSchemaManager($manipulator); + } + + /** + * Mock a schema information for the passed doctrine schema information. + * + * @param DoctrineSchemaInformation|null $information The doctrine information if any. + * + * @return SchemaInformation + */ + private function mockSchemaInformation(DoctrineSchemaInformation $information = null): SchemaInformation + { + /** @var MockObject|SchemaInformation $schemaInformation */ + $schemaInformation = $this->getMockBuilder(SchemaInformation::class)->getMock(); + + if (null === $information) { + $schemaInformation + ->expects($this->once()) + ->method('has') + ->with(DoctrineSchemaInformation::class) + ->willReturn(false); + $schemaInformation + ->expects($this->never()) + ->method('get'); + + return $schemaInformation; + } + + $schemaInformation + ->expects($this->once()) + ->method('has') + ->with(DoctrineSchemaInformation::class) + ->willReturn(true); + $schemaInformation + ->expects($this->once()) + ->method('get') + ->with(DoctrineSchemaInformation::class) + ->willReturn($information); + + return $schemaInformation; + } +} diff --git a/tests/Schema/LegacySchemaGeneratorTest.php b/tests/Schema/LegacySchemaGeneratorTest.php new file mode 100644 index 000000000..a306f297d --- /dev/null +++ b/tests/Schema/LegacySchemaGeneratorTest.php @@ -0,0 +1,146 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Schema\Doctrine; + +use MetaModels\Attribute\IAttribute; +use MetaModels\Attribute\IComplex; +use MetaModels\Attribute\IInternal; +use MetaModels\Attribute\ISimple; +use MetaModels\IFactory; +use MetaModels\IMetaModel; +use MetaModels\Information\AttributeInformation; +use MetaModels\Information\MetaModelCollectionInterface; +use MetaModels\Information\MetaModelInformation; +use MetaModels\Schema\LegacySchemaGenerator; +use MetaModels\Schema\LegacySchemaInformation; +use MetaModels\Schema\SchemaInformation; +use PHPUnit\Framework\TestCase; + +/** + * This tests the doctrine schema. + * + * @covers \MetaModels\Schema\LegacySchemaGenerator + * + * @SuppressWarnings(PHPMD.CouplingBetweenObjects) + */ +class LegacySchemaGeneratorTest extends TestCase +{ + /** + * Test the instantiation. + * + * @return void + */ + public function testInstantiation(): void + { + $instance = new LegacySchemaGenerator($this->getMockForAbstractClass(IFactory::class), []); + + $this->assertInstanceOf(LegacySchemaGenerator::class, $instance); + } + + /** + * Test the generate method. + * + * @return void + */ + public function testGenerateAddsSchemaInformationIfNotFound(): void + { + $instance = new LegacySchemaGenerator($this->getMockForAbstractClass(IFactory::class), []); + $information = new SchemaInformation(); + $collection = $this->getMockForAbstractClass(MetaModelCollectionInterface::class); + + $collection->expects($this->once())->method('getIterator')->willReturn(new \ArrayIterator([])); + + $instance->generate($information, $collection); + + $this->assertTrue($information->has(LegacySchemaInformation::class)); + } + + /** + * Test the generate method. + * + * @return void + */ + public function testGenerate(): void + { + $information = new SchemaInformation(); + $collection = $this->getMockForAbstractClass(MetaModelCollectionInterface::class); + + $attribute1 = $this->mockAttribute(ISimple::class, 'attribute1'); + $attribute2 = $this->mockAttribute(IComplex::class, 'attribute2'); + $attribute3 = $this->mockAttribute(ISimple::class, 'managed-type'); + $attribute4 = $this->getMockForAbstractClass(IInternal::class); + $metaModel = $this->mockMetaModel([$attribute1, $attribute2, $attribute3, $attribute4]); + + $factory = $this->getMockForAbstractClass(IFactory::class); + $factory->expects($this->once())->method('getMetaModel')->with('mm_test')->willReturn($metaModel); + + $collection->expects($this->once())->method('getIterator')->willReturn(new \ArrayIterator([ + $metaModelInformation = new MetaModelInformation('mm_test') + ])); + $metaModelInformation->addAttribute(new AttributeInformation('test', 'test_type')); + + $instance = new LegacySchemaGenerator($factory, ['managed-type']); + + $instance->generate($information, $collection); + + $this->assertTrue($information->has(LegacySchemaInformation::class)); + $this->assertSame( + [$attribute1, $attribute2], + $information->get(LegacySchemaInformation::class)->getAttributes() + ); + } + + /** + * Mock a MetaModel with the passed attributes. + * + * @param IAttribute[] $attributes The attributes. + * + * @return IMetaModel + */ + private function mockMetaModel(array $attributes) + { + $metaModel = $this->getMockForAbstractClass(IMetaModel::class); + + $metaModel->expects($this->once())->method('getAttributes')->willReturn($attributes); + + return $metaModel; + } + + /** @param class-string $interface */ + private function mockAttribute(string $interface, string $typeName): IAttribute + { + $attribute = $this->getMockForAbstractClass($interface); + + $attribute + ->expects(self::atLeastOnce()) + ->method('get') + ->willReturnCallback(function (string $key) use ($typeName) { + switch ($key) { + case 'type': + return $typeName; + } + throw new \LogicException('Unexpected get call'); + }); + + return $attribute; + } +} diff --git a/tests/Schema/LegacySchemaInformationTest.php b/tests/Schema/LegacySchemaInformationTest.php new file mode 100644 index 000000000..aca52effc --- /dev/null +++ b/tests/Schema/LegacySchemaInformationTest.php @@ -0,0 +1,66 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Schema\Doctrine; + +use MetaModels\Attribute\IAttribute; +use MetaModels\Attribute\IComplex; +use MetaModels\Attribute\ISimple; +use MetaModels\Schema\LegacySchemaInformation; +use PHPUnit\Framework\TestCase; + +/** + * This tests the legacy schema. + * + * @covers \MetaModels\Schema\LegacySchemaInformation + */ +class LegacySchemaInformationTest extends TestCase +{ + /** + * Test the instantiation. + * + * @return void + */ + public function testInstantiation(): void + { + $instance = new LegacySchemaInformation(); + + $this->assertInstanceOf(LegacySchemaInformation::class, $instance); + $this->assertSame(LegacySchemaInformation::class, $instance->getName()); + $this->assertSame([], $instance->getAttributes()); + } + + /** + * Test adding of attributes. + * + * @return void + */ + public function testAddAttributes(): void + { + $instance = new LegacySchemaInformation(); + + $instance->addAttribute($attribute1 = $this->getMockForAbstractClass(ISimple::class)); + $instance->addAttribute($attribute2 = $this->getMockForAbstractClass(IComplex::class)); + $instance->addAttribute($attribute3 = $this->getMockForAbstractClass(IAttribute::class)); + + $this->assertSame([$attribute1, $attribute2, $attribute3], $instance->getAttributes()); + } +} diff --git a/tests/Schema/LegacySchemaManagerTest.php b/tests/Schema/LegacySchemaManagerTest.php new file mode 100644 index 000000000..802f8a3d3 --- /dev/null +++ b/tests/Schema/LegacySchemaManagerTest.php @@ -0,0 +1,120 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Schema\Doctrine; + +use MetaModels\Attribute\IAttribute; +use MetaModels\Attribute\IComplex; +use MetaModels\Attribute\ISimple; +use MetaModels\Schema\LegacySchemaInformation; +use MetaModels\Schema\LegacySchemaManager; +use MetaModels\Schema\SchemaInformation; +use PHPUnit\Framework\TestCase; + +/** + * This tests the doctrine schema. + * + * @covers \MetaModels\Schema\LegacySchemaManager + */ +class LegacySchemaManagerTest extends TestCase +{ + /** + * Test the instantiation. + * + * @return void + */ + public function testInstantiation(): void + { + $instance = new LegacySchemaManager(); + + $this->assertInstanceOf(LegacySchemaManager::class, $instance); + } + + /** + * Test the generate method. + * + * @return void + */ + public function testIgnoresIfNotFound(): void + { + $instance = new LegacySchemaManager(); + $information = $this->getMockBuilder(SchemaInformation::class)->getMock(); + $information->expects($this->once())->method('has')->with(LegacySchemaInformation::class)->willReturn(false); + $information->expects($this->never())->method('get'); + + $instance->process($information); + } + + /** + * Test the process method. + * + * @return void + */ + public function testProcess(): void + { + $information = new SchemaInformation(); + $legacy = new LegacySchemaInformation(); + $information->add($legacy); + + $legacy->addAttribute($attribute1 = $this->getMockForAbstractClass(ISimple::class)); + $legacy->addAttribute($attribute2 = $this->getMockForAbstractClass(IComplex::class)); + $legacy->addAttribute($attribute3 = $this->getMockForAbstractClass(IAttribute::class)); + + $attribute1->expects($this->once())->method('initializeAUX'); + $attribute2->expects($this->once())->method('initializeAUX'); + $attribute3->expects($this->once())->method('initializeAUX')->willThrowException(new \RuntimeException()); + + $instance = new LegacySchemaManager(); + + $instance->process($information); + } + + /** + * Test the validate method. + * + * @return void + */ + public function testValidate(): void + { + $information = new SchemaInformation(); + $legacy = new LegacySchemaInformation(); + $information->add($legacy); + + $legacy->addAttribute($attribute1 = $this->getMockForAbstractClass(ISimple::class)); + $legacy->addAttribute($attribute2 = $this->getMockForAbstractClass(IComplex::class)); + $legacy->addAttribute($attribute3 = $this->getMockForAbstractClass(IAttribute::class)); + + $attribute1->expects($this->once())->method('getColName')->willReturn('attribute1'); + $attribute1->expects($this->once())->method('get')->with('type')->willReturn('type1'); + $attribute2->expects($this->once())->method('getColName')->willReturn('attribute2'); + $attribute2->expects($this->once())->method('get')->with('type')->willReturn('type2'); + $attribute3->expects($this->once())->method('getColName')->willReturn('attribute3'); + $attribute3->expects($this->once())->method('get')->with('type')->willReturn('type3'); + + $instance = new LegacySchemaManager(); + + $this->assertSame([ + '(Re-)Initialize attribute "attribute1" (type: "type1") via legacy method.', + '(Re-)Initialize attribute "attribute2" (type: "type2") via legacy method.', + '(Re-)Initialize attribute "attribute3" (type: "type3") via legacy method.', + ], $instance->validate($information)); + } +} diff --git a/tests/Schema/SchemaGeneratorTest.php b/tests/Schema/SchemaGeneratorTest.php new file mode 100644 index 000000000..2fbbb0813 --- /dev/null +++ b/tests/Schema/SchemaGeneratorTest.php @@ -0,0 +1,65 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Schema; + +use MetaModels\Information\MetaModelCollectionInterface; +use MetaModels\Schema\SchemaGenerator; +use MetaModels\Schema\SchemaGeneratorInterface; +use MetaModels\Schema\SchemaInformation; +use PHPUnit\Framework\TestCase; + +/** + * This tests the schema generator class. + * + * @covers \MetaModels\Schema\SchemaGenerator + */ +class SchemaGeneratorTest extends TestCase +{ + /** + * Test the instantiation. + * + * @return void + */ + public function testInstantiation(): void + { + $instance = new SchemaGenerator([]); + + $this->assertInstanceOf(SchemaGenerator::class, $instance); + } + + /** + * Test generating the schema. + * + * @return void + */ + public function testGenerateSchema(): void + { + $collection = $this->getMockForAbstractClass(MetaModelCollectionInterface::class); + $information = new SchemaInformation(); + $generator = $this->getMockForAbstractClass(SchemaGeneratorInterface::class); + $generator->expects($this->once())->method('generate')->with($information, $collection); + + $instance = new SchemaGenerator([$generator]); + + $instance->generate($information, $collection); + } +} diff --git a/tests/Schema/SchemaInformationTest.php b/tests/Schema/SchemaInformationTest.php new file mode 100644 index 000000000..4184d4064 --- /dev/null +++ b/tests/Schema/SchemaInformationTest.php @@ -0,0 +1,128 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Schema; + +use MetaModels\Schema\SchemaInformation; +use MetaModels\Schema\SchemaInformationInterface; +use PHPUnit\Framework\TestCase; + +/** + * This tests the schema information. + * + * @covers \MetaModels\Schema\SchemaInformation + */ +class SchemaInformationTest extends TestCase +{ + /** + * Test the instantiation. + * + * @return void + */ + public function testInstantiation(): void + { + $instance = new SchemaInformation(); + + $this->assertInstanceOf(SchemaInformation::class, $instance); + } + + /** + * Test adding. + * + * @return void + */ + public function testAdd(): void + { + $instance = new SchemaInformation(); + + $mock = $this->getMockForAbstractClass(SchemaInformationInterface::class); + $mock->method('getName')->willReturn('test'); + + $instance->add($mock); + + $this->assertTrue($instance->has('test')); + $this->assertSame($mock, $instance->get('test')); + } + + /** + * Test adding. + * + * @return void + */ + public function testAddThrowsForAlreadyRegistered(): void + { + $instance = new SchemaInformation(); + + $mock = $this->getMockForAbstractClass(SchemaInformationInterface::class); + $mock->method('getName')->willReturn('test'); + + $instance->add($mock); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Information with name "test" already registered.'); + + $instance->add($mock); + } + + /** + * Test adding. + * + * @return void + */ + public function testGetThrowsForUnregistered(): void + { + $instance = new SchemaInformation(); + + $this->expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage('Information with name "test" not registered.'); + + $instance->get('test'); + } + + /** + * Test the has method. + * + * @return void + */ + public function testHasForUnknown(): void + { + $instance = new SchemaInformation(); + + $this->assertFalse($instance->has('unknown')); + } + + /** + * Test obtaining the list of registered names. + * + * @return void + */ + public function testGetRegisteredNames(): void + { + $instance = new SchemaInformation(); + + $mock = $this->getMockForAbstractClass(SchemaInformationInterface::class); + $mock->method('getName')->willReturn('test'); + + $instance->add($mock); + + $this->assertSame(['test'], $instance->getRegisteredNames()); + } +} diff --git a/tests/Schema/SchemaManagerTest.php b/tests/Schema/SchemaManagerTest.php new file mode 100644 index 000000000..0527c774d --- /dev/null +++ b/tests/Schema/SchemaManagerTest.php @@ -0,0 +1,119 @@ + + * @copyright 2012-2019 The MetaModels team. + * @license https://github.com/MetaModels/core/blob/master/LICENSE LGPL-3.0-or-later + * @filesource + */ + +declare(strict_types=1); + +namespace MetaModels\Test\Schema; + +use MetaModels\Schema\SchemaInformation; +use MetaModels\Schema\SchemaManager; +use MetaModels\Schema\SchemaManagerInterface; +use PHPUnit\Framework\TestCase; + +/** + * This tests the schema manager class. + * + * @covers \MetaModels\Schema\SchemaManager + */ +class SchemaManagerTest extends TestCase +{ + /** + * Test the instantiation. + * + * @return void + */ + public function testInstantiation(): void + { + $instance = new SchemaManager([]); + + $this->assertInstanceOf(SchemaManager::class, $instance); + } + + /** + * Test processing. + * + * @return void + */ + public function testPreprocess(): void + { + $manager1 = $this->getMockForAbstractClass(SchemaManagerInterface::class); + $manager2 = $this->getMockForAbstractClass(SchemaManagerInterface::class); + $information = new SchemaInformation(); + $manager1->expects($this->once())->method('preprocess')->with($information); + $manager2->expects($this->once())->method('preprocess')->with($information); + + $instance = new SchemaManager([$manager1, $manager2]); + + $instance->preprocess($information); + } + + /** + * Test processing. + * + * @return void + */ + public function testProcess(): void + { + $manager1 = $this->getMockForAbstractClass(SchemaManagerInterface::class); + $manager2 = $this->getMockForAbstractClass(SchemaManagerInterface::class); + $information = new SchemaInformation(); + $manager1->expects($this->once())->method('process')->with($information); + $manager2->expects($this->once())->method('process')->with($information); + + $instance = new SchemaManager([$manager1, $manager2]); + + $instance->process($information); + } + + /** + * Test processing. + * + * @return void + */ + public function testPostcess(): void + { + $manager1 = $this->getMockForAbstractClass(SchemaManagerInterface::class); + $manager2 = $this->getMockForAbstractClass(SchemaManagerInterface::class); + $information = new SchemaInformation(); + $manager1->expects($this->once())->method('postprocess')->with($information); + $manager2->expects($this->once())->method('postprocess')->with($information); + + $instance = new SchemaManager([$manager1, $manager2]); + + $instance->postprocess($information); + } + + /** + * Test validation. + * + * @return void + */ + public function testValidate(): void + { + $manager1 = $this->getMockForAbstractClass(SchemaManagerInterface::class); + $manager2 = $this->getMockForAbstractClass(SchemaManagerInterface::class); + $information = new SchemaInformation(); + $manager1->expects($this->once())->method('validate')->with($information)->willReturn(['1', '2']); + $manager2->expects($this->once())->method('validate')->with($information)->willReturn(['3', '4']); + + $instance = new SchemaManager([$manager1, $manager2]); + + $this->assertSame(['1', '2', '3', '4'], $instance->validate($information)); + } +}